#lightyear
1 messages · Page 13 of 1
I’m just running the deterministic replication example with host client mode
I only have issues with host-client mode in that example. All the other examples and modes work fine
@pine cape This ack message is a little too loud, I think it is triggering everytime a packet becomes too chonki
This is fixed in main
i luv u
the fact that Confirmed can be added after some components is a bit annoying, gotta study a way to make the ordering of the components being added a bit more strict, maybe do something like this when receiving a replication message
commands.spawn((Confirmed, <other lightyyear control components>)).insert((<replicated components>));
hmm, Confirmed is not a strict component for an entity, it is only added if the entity has ShouldBePredicted https://github.com/cBournhonesque/lightyear/blob/d92bdf670421cf08c75021d4b87b861f88b169b4/lightyear_prediction/src/spawn.rs#L17-L71
(
Without<Confirmed>,
Without<ShouldBePredicted>,
Without<ShouldBeInterpolated>,
)
this works now
@pine cape updated the PR to fix the player movement query as it wasn't working in host-client mode https://github.com/cBournhonesque/lightyear/pull/1136
host-client mode works now but eventually has checksum errors
Hi, im trying to replicate my clients input on the server.
this is my protocol enum
#[protocol(
Event = "MyEvent",
Input = "PlayerInput",
Component = "MyComponent",
Message = "MyMessage"
)]
pub enum MyProtocol {
// Input messages
#[message(Instant)]
PlayerInput(PlayerInput),
// Replicated components
#[component(Sync)]
Player(Player),
#[component(Sync)]
Transform(Transform),
#[message(Channel = "Channel1")]
Message1(Message1),
}
But the compiler cant find the macros in any crate.
Is this the right way to do it?
i've never seen those attributes
yeah I looked through the repo and I couldnt find them either. Full disclosure I got them from Gemini...
I've been trying to get it done for a few days...
This is from a very old version of lightyear, I would advise you to use the latest version and check the tutorial
@earnest fog I think i'll revert this commit: https://github.com/cBournhonesque/lightyear/pull/1135
I'm a bit hesitant of masking errors like this if it can be avoided.
In practice, we don't need to add the ChecksumSend plugin on the HostClient since the app is also a server, so what i'll do instead is not add the ChecksumSend plugin if the server feature is enabled. It's not a perfect solution since it means that non-host client will have to run with the server feature disabled, but then no changes are required on the registry
The deterministic-replication example now runs in host-client mode
Interestingly, inputs from the host-client work fine, but inputs from the remote client cause a checksum mismatch
which points to a bug as to how inputs are handled for host-client mode
Probably something with input-delay, or with the delay-buffer
I don't have the bandwidth to debug this now, but i'll open an issue
I can look into over the next few days. Thanks for getting the example working again
thanks; might be hard to debug though :p
I guess the first step would be to print the input buffer at every tick and see where is the mismatch
Hey,
thanks for all of the work already put into this crate!
I am wondering if there is already a way to get notified about a certain component replication update using observers.
Something similar like Trigger<OnReplicated, COMPONENT>
I would use OnAdd<C> and add a With<Replicated> filter
Is there a minimal example for using this crate? I tried copying the simple box example to a standalone project but it has so many dependencies that it feels like too much work trying to get it to work.
The simple_setup example
I guess this has already been asked before but why is the client receiving packets so long after the server on a local connection? It looks like a few hundred ms. The server doesn't have any noticeable lag.
The component is already present but one field changes sometime during the simulation and I would like to react to this change without creating an additional event.
In the examples? Because there is a link conditioner that simulates network latency.
Also components are replicated at a replication_interval, which is 100ms in the examples
I had this before but it was fairly expensive when 99.9% of all events are not used; maybe there could be an API where you could register getting events for a given entity/component
I've been thinking about the message registration and new limitation with host-client mode. I've got 2 ideas:
- Add a check to the
ChecksumReceivePluginto check ifChecksumSendPluginis already registered. If true, don't register theChecksumMessageagain. - Add a new
ChecksumPluginthat does theChecksumMessageregistration and does the hackyis_uniquePlugin workaround to make sure it's not run twice.
Thoughts?
I think 2. works; but what about just having a if !register.is_registered::<M>() { register }
Maybe we could specify this using an additional function in ComponentRegistration?
Something like add_replication_notify / add_replication_event (or a better name)?
And then listen for this using Trigger<OnReplicated, COMPONENT>
Yeah, the simple_box. What I want is basically as soon as the client inputs something, that should be sent to the server.
That is the case already, inputs are sent as soon as possible
How do i add an InputBuffer to my character? The component isnt automatically added, but the examples uses it without doing any setup, so how do i add it? (leafwing input)
Everything is added for you when you add an InputMap component on the entity you want to control
my character contains an InputMap, but doesn't have an InputBuffer
InputPlugin is added on both the server and client
I see InputBuffer in your screenshot
In regards to: https://github.com/cBournhonesque/lightyear/issues/1124
steam and crossbeam do not work together right now. I'm thinking I like option 1 for now, if you point me in the right direction I can try to make a pullrequest this weekend
It's basically just adding a Without<SteamServerIO> on the filters for the NetcodePlugin systems: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_netcode/src/server_plugin.rs#L104
Hm actually no because we still want Netcode to be enabled for non-steam clients.
We probably need to add a marker component here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_steam/src/server.rs#L140 that marks the LinkOf entity as using Steam; and then in the Netcode queries we can ignore LinkOfs that have that component
Yeah i think option 1 (one server entity that has multiple ServerIo components is probably easier)
Ok we got it working, sent a pr last night!
So I’m at the point where I need to debug latency issues. Over steam I get about 60 ms of latency and the rollbacks are crazy. Adding a conditioner with 60 ms of latency does not replicate these rollbacks over udp
what is your real latency? you can get it with debug logs in lightyear_sync i believe
or by adding the diagnostics
It is “lightyear_sync=debug” or lightyear::sync?
lightyear_sync=debug
its telling me a delta of -12 ticks, no latency print out
ill try diagnostics feature
although I am getting a lot of printouts saying rollbacks over 200 ticks
You can also add
#[cfg(feature = "visualizer")]
{
app.add_plugins(bevy_metrics_dashboard::RegistryPlugin::default());
app.add_plugins(bevy_metrics_dashboard::DashboardPlugin);
app.add_systems(Startup, |mut commands: Commands| {
commands.spawn(bevy_metrics_dashboard::DashboardWindow::new("Metrics"));
});
}
and enable the metrics feature of lightyear
to add the metrics dashboard
ooh, where in the namespace is latency?
ah is it not there? damn
Looks like it says 60ms still; are you using steam locally?
yeah its between my mac and windows computer, one is on wifi.
wouldn't steam connections always not be local because they use a server to get past port forwarding?
Probably; 60ms is pretty low and shouldn't cause any issues.
You're doing something similar to simple_box? state-replication with prediction?
You disabled the link conditioner?
@pine cape host-client message registration check and a couple of other minor fixes https://github.com/cBournhonesque/lightyear/pull/1145
I'm playing with messages and I've got it going host-client -> client but not the other way around. It's registered bidirectionally.
Do I need to do something in the on_new_client handler. I thought may I have to manually add MessageReciever<MyMessage> to the Connected but I don't see that in the examples.
Actually it seems to be auto added. I'm guessing its a logic error in my code.
actually it seems it's something about how host-client works.
pub(crate) fn receive(
mut receiver: Query<
&mut lightyear::prelude::MessageReceiver<InteractionMessage>,
With<Client>,
>,
mut commands: Commands,
) {
for message in receiver.single_mut().unwrap().receive() {
// THIS does not
info!("Client received message: {:?}", message);
...
}
}
app.add_systems(
Update,
receive
.in_set(lightyear::prelude::MessageSet::Receive)
);
pub(crate) fn server_receive_and_send(
receiver: Query<(
&mut lightyear::prelude::RemoteId,
&mut lightyear::prelude::MessageReceiver<InteractionMessage>,
)>,
mut sender: ServerMultiMessageSender,
server: Single<&lightyear::prelude::Server>,
) -> Result {
for (remote_id, mut receiver) in receiver {
for message in receiver.receive() {
// THIS runs
info!("Server received message: {:?}", message);
// I expect this to send to the local client (when remote id is remote client)
sender.send::<_, ActionsChannel>(
&message,
&*server,
&NetworkTarget::AllExceptSingle(remote_id.0),
)?;
}
}
Ok(())
}
app.add_systems(
Update,
server_receive_and_send
.in_set(lightyear::prelude::MessageSet::Receive)
.run_if(is_server),
);
client sends a message to host-client, and it is recieved by server_receive_and_send but the resend there is not received by the local client.
expected?
Let's say you have HC (host-client) and C (remote client).
You're saying that C sends a message to the server; then the server sends a message to all clients (HC and C), but HC doesn't receive it?
It should work, based on this unit test: https://github.com/cBournhonesque/lightyear/blob/87129fc553795cb41e33003a6b9d61e0d53b225c/lightyear_tests/src/host_server/messages.rs#L67-L67
okay, I will look at that and dig deeper
I notice that in my test i'm using
sender
.send::<_, Channel1>(&send_message_clone, unsafe {
UniqueEntityArray::from_array_unchecked([client_of_0, host_client])
})
.ok();
it's possible that there's a bug when using NetworkTarget
Can you confirm that your HC has a MessageManager and Transport components?
hm you're right I think there is a bug
Client entity does.
and MessageReceiver<InteractionMessage>
NetworkTarget::All has the same issue
Oh, I was meaning to ask about reconnects also. client reconnect results in this on server side for me
2025-08-04T03:31:37.911313Z WARN lightyear_netcode::error: Netcode error: Packet(TokenExpired)
You have to generate a new ConnectToken or set a negative expiration value here: https://github.com/cBournhonesque/lightyear/blob/87129fc553795cb41e33003a6b9d61e0d53b225c/lightyear_netcode/src/client_plugin.rs#L68-L68
I added a unit test for it; it seems to work
I have a server_app with a HostClient entity + a ClientOf entity (remote client).
I send a message from the server to both of them using the ServerMultiMessageSender, and both clients receive it.
Hm one thing that is different is that you send the messages in Update, i do outside of the app updating
that could cause an issue
hmm my debugger does not understand how to show Entity
so I'm recompiling with some dbgs in send_with_priority to see what its doing
no, still works if i send in Update
you should be hitting this log: https://github.com/cBournhonesque/lightyear/blob/09a095662a567439d127447cbd137af3cc49cb7d/lightyear_messages/src/receive.rs#L280-L280
basically it's suboptimal, but we serialize the bytes and buffer them in the Transport of the host-client entity in MultiMessageSender::send
and then in Plugin::recv, we deserialize the bytes and insert them into the MessageReceiver of the host-client entity
I don't get that log
You should try to confirm that the message is being written to the Transport by the ServerMultiMessageSender
This line runs, and sender= entity id of the Client
could it be that I'm using the ActionsChannel?
looks like I missed this
and that fixes it
🎉
2025-08-04T04:54:35.970400Z TRACE lightyear_messages::receive: Received message "lightyear_sync::ping::message::Ping" on channel ChannelKind(TypeId(0xe2e202f912727d2cda9e6c573a3cc731))
2025-08-04T04:54:35.970406Z TRACE lightyear_messages::receive: Received message (id:None) from peer Netcode(17434101101550735348) on channel ChannelKind(TypeId(0xe2e202f912727d2cda9e6c573a3cc731)). 364v1#4294967660
2025-08-04T04:54:35.970411Z TRACE lightyear_messages::receive: Received message "lightyear_sync::ping::message::Pong" on channel ChannelKind(TypeId(0xe2e202f912727d2cda9e6c573a3cc731))
2025-08-04T04:54:35.970414Z TRACE lightyear_messages::receive: Received message (id:None) from peer Netcode(17434101101550735348) on channel ChannelKind(TypeId(0xa47bc931a4d47c60a39abfa8be5b5093)). 364v1#4294967660
I am curious what the id: None message is, and whether it's possible to reduce the rate of ping and pong.
setting keepalive_packet_send_rate: 1 doesn't seem to reduce freq, I am assuming that's a different layer.
seems I can do this in the ChannelRegistry, any reason not to? EDIT: this does not seem to reduce freq of ping/pong either
thread 'tokio-runtime-worker' panicked at /home/user/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wtransport-0.6.1/src/error.rs:122:18:
QUIC connection is still alive on close-cast
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
2025-08-04T05:22:49.351476Z INFO lightyear_netcode::client: client connecting to server 127.0.0.1:42217 [1/1]
2025-08-04T05:22:49.351501Z WARN lightyear_webtransport::client: Connecting with no certificate validation
2025-08-04T05:22:53.697418Z ERROR lightyear_netcode::client: client ignored packet: invalid packet: sequence 4611686018427387905 already received
... (^^repeats ~100 times)
2025-08-04T05:22:54.918083Z ERROR lightyear_netcode::client: client ignored packet: invalid packet: sequence 4611686018427388014 already received
2025-08-04T05:22:54.934796Z INFO lightyear_netcode::client_plugin: Client Netcode(1885806082525081831) connected
2025-08-04T05:23:19.926165Z ERROR lightyear_netcode::client: client ignored packet: invalid packet: sequence 4611686018427388271 already received
another reconnect issue is I get this on client when the server is killed+restarted and client reconnects
For unsequenced unreliable channels, I don't assign an ID to the message, since it's not used
You can reduce the frequency of pings in the PingManager component; but I wouldn't recommend it; pings are used to compute the RTT/jitter of the connection and to keep clients in sync
but the client ends up reconnecting, right?
it depends.
it has to get through a lot of those invalid packet messages
the amount of them seems related to how long the server was down for
and so sometimes it just keeps printing that error, on the order of 10 times per second, sometimes for a long time
Are there any actual simple self-contained examples for lightyear?
All the examples in the github repository use lightyear_examples_common
No, I've wanted that before as well
I wonder if some stuff from lightyear_examples_common could be moved into a lightyear crate for general use.
the official start guide also has missing and outdated sections
are there other resources i could use?
There is one, it's called 'simple_setup', but it just starts the connection
thanks, this one's really helpful
shows the missing parts
i am following the examples and the input buffering does not work
the if let block does't run, when unwrapping the error it claims there are no entities
i have registered the type and plugin and i am running it in the correct system
is there something i've forgotten?
You have add an InputMarker component to your predicted entitu
i dont have a predicted entity yet, only the server one
is a predicted entity needed to send input to the server?
inserting it into the server entity does nothing
the movement method now logs movement but still does not receive input
ive tried to add prediction and interpolation and the entities dont appear
and the red square is still visible, so it seems to not have the Confirmed component
the Added predicition info is displayed but the if let block is not ran
No, you can also add InputMarker on the Replicated entity I believe
You're inserting InputMarker on the server, but you need to insert it on entities that are on the client app
Basically the client needs to know among all replicated client entities, which one is the only they control
Did you add a PredictionManager component on your client entity? Is PlayerColor registered in the protocol?
Ive added interpolation and prediction (ive fixed the issue from my last messages) i was missing the PredictionManager and InterpolationManager
my newest issue is that while the sharedMovementBehavior runs on both server and client, neither shows any movement
yeah prediction manager was the issue
Looks like your movement system is not actually modifying the position?
it was the issue, i used deref on the system parameter which i did not need to
im surprised it showed no errors
@pine cape i added .add_map_entities to a trigger, but using both the Entity of the predicted and confirmed entities fail
on the server the Entity is still the client Entity
fixed
missed #[entities] on the MapEntities derive
There is https://github.com/SueHeir/lightyear-menu for a steam game example:) Allows you to host a server in a background thread
its a little more complex than a simple example, and I wrote it so the code smells bad, but its self contained
Very cool example, I'll definitely need to do that in the future
Thanks!
@pine cape moving ChannelSettings and ChannelMode to lightyear_core possible?
more like, does it make sense?
What would be the reason? I'd prefer to keep it in lightyear_transport unless there's a good reason
@pine cape is it possible to change the InterpolationConfig externally?
I saw you have a TODO to move it to InterpolationManager but I'm struggling to find a way to set it
Hm you could disable the interpolation feature from lightyear, and the InterpolationPlugin directly from lightyear_interpolation
but yeah i think the correct solution is to move the InterpolationConfig to the InterpolationManager
hm looking at the code; it looks like there's multiple InterpolationConfigs?
One thing you could is simply update the InterpolationConfig in the InterpolationTimeline component before you connect the client.
fn set_config(timeline: Single<&mut InterpolationTimeline>) {
timeline.interpolation_config = ...
}
great thanks!
Anyone figured out how to do replication with the new BEI api yet? ive been putting off having a crack at it because the only idea ive got is to replicate the actual action entities.
I tried to implement with that approach (spawning the Action entities on the server when they receive an InputMessage component) but i've had trouble making it fit into my shared lightyear_inputs framework that abstracts over BEI, leafwing and native
primarily because the new BEI uses 4 components instead of 1 to represent the ActionState
Anyone know how to get your bandwidth usage to show up in the lightyear egui metrics panel?
Also has anyone done any live tests with 5+ people yet?
I did a live test during the previous game jam
@pine cape Hey sorry to bother u nice to see you again mr peri, anyways i am doing lag compensation for my player entity, an entity that is predicted on it is client and interpolated on other. WIll lagcompensation history work normally in that scenario? Just curious
Yes that's how lag compenstaion works in the fps example: https://github.com/cBournhonesque/lightyear/blob/main/examples/fps/src/server.rs#L122
oh thats awesome!! I'm guessing it went well?
yep, everything worked 🙂
@pine cape There is a race condition going at https://github.com/cBournhonesque/lightyear/blob/cbcb42825de980430a12dfa56c49f34faea03473/lightyear_avian/src/lag_compensation/history.rs#L178
If you dont spawn an entity immediately with the lag compensation history (add it via observer on a later stage, or via system), for a single frame this system runs causing a crash due to the unchecked unwrap.
@pine cape https://github.com/cBournhonesque/lightyear/pull/1152 PR
thanks, can you only include history.rs in the PR?
@pine cape Removed vscode file and made it gitignore future ones (if that is okay by you), btw would you mind releasing a new version. This fix is kinda really important for me.
I need to wait for a new release of bevy-enhanced-input since i want the next lightyear release to be compatible with it. In the meantime you can just use the main branch of lightyear, no?
@dusk forum I got the latest BEI working, will release a new version once BEI publish a new version with a couple necessary changes
It looks like you are despawning an Interpolated entity that doesn't have a corresponding Confirmed entity with a Replicated component; is that intended?
My server query in the system that deletes it just checks for the Bullet marker component, nothing else
Would that be the issue
I have traumas related to using mains 🙂
your server query? the interpolated entity exists on the server?
Anyway this is the log: https://github.com/cBournhonesque/lightyear/blob/cb%2Fupgrade-bei/lightyear_interpolation/src/lib.rs#L71-L71
So i am randomly getting these errors
thread 'Compute Task Pool (15)' panicked at C:\Users\Elizabeth\.cargo\git\checkouts\lightyear-16a1ca81dacb5ed5\2711a08\lightyear_inputs\src\input_buffer.rs:153:37:
attempt to subtract with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `lightyear_inputs::server::receive_input_message<lightyear_inputs_leafwing::input_message::LeafwingSequence<bevy_code_gnome::networking::protocol::PlayerActions>>`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
Do you think its something I did in my code or should we check the subract here?
legend
I get these too
I missed that, it's probably a real error where we I need to use wrapped_sub somewhere
Also started getting this with steam:
2025-08-11T03:56:02.049649Z WARN aeronet_io::packet: 17854v3 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer
@pine cape Mr Peri my game is reaching the point where well, we are getting oom issues. Of course I can try to diminish the main offenders, but I think is about time we deploy a server. Any ideas on cheap ways to do so?
I've been hosting my lightyear server on fly.io and it worked pretty well for my use case
The cheapest might be to buy hardware and physically host it somewhere. The next option is to rent one or more VPSes/dedis from one of the cheaper providers (Hetzner for example)
Fly is really overpriced though
that's fair, depends on the workload for a demo it's not very expensive to pay 3$/m for a shared 2cpu which in my case could handle multiple dozens of users
Hmm I have a few good rapsberries
Perhaps i can use them
There are plenty of providers that give you 1month of free credit (kamatera, digital ocean, hertzner, etc.) That's usually what I do
They seem to only happen with crossbeamIo
Might be a setup issue, might be the 3 ms latency causing the problem
Are you using the latest version? I think it's because some filters are missing in the crossbar systems so some packets intended for steam are getting stolen by crossbeam
yeah its the latest version, so you think crossbeamIO needs it's own "this is a crossbeamIO link" component?
it seems like crossbeam has that already, every function is filtered by CrossbeamIO
ohh this error is from aeronet not lightyear, i see
17854v1 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer
ok this is crazy, steam works when I use my gui-server, and does not work when I use my no gui server
these errors started showing up when I rewrote stuff to run servers without bevy_render
It's because without gui the frame rate is super high
So you're sending more packets and there's a higher chance of the error appearing
@pine cape fix host-client message registration https://github.com/cBournhonesque/lightyear/pull/1145
ive been toying around with the idea of implementing a version of dynamic server meshing (i have some theories on which parts of a simulation you can get away with high latencies on and players not feel it, which only really matter if you have basically meshing across large physical distances) using a scale to zero provider like fly for quite awhile now.
how you been liking them?
I've been generally happy with it. I hosted some web services with them for a while now and I've had no issues. As for lightyear, I've only been toying with the library for the past week and wanted to test something hosted and it's just so easy to have this deployed in seconds so that's mainly why I used them. As someone else said before they can be expensive so for scaling or doing more advanced stuff prob look if this is a dealbreaker & they have a note about MTUs which I'm really not sure if this is even relevant for lightyear but useful to be mindful of I guess?
locking the framerate does not seem to fix the issue:(
yeah when i was looking into it originally, it was for everyones favourite indie dev time sinkhole, an mmo 
So i could justify the price away in my head with "monthly sub would cover it if im only ever paying for servers when someones playing and thus paying"
theres definitely cheaper ways to do hosting 
what are you using besides crossbeam? steam?
and is the error on the client or serveR?
Also does this happen when clients are connected? or around connection/disconnection?
lightyear has a max MTU of 1200 bytes so you won't run into this
I can disable everything except steam and the error is still happening, the warning happens on the server, and they are technically connected,
@peak ice long shot, but i had the same error. I dont know why this fixed it for me ☝️ i havent seen that error since.. very strange bug at least the way i was triggering it
I don't use enchanted input plugin, but maybe it has something to do with inputs?
it started with observers with inputs for me.. maybe not related then?
enchanted 😂 can you please open an issue so we don't lose track of this?
lol
I made the issue, also its currently happening in the lightyear-menu example when I switch to a true headless mode
#crates message
What's the modern alternative for DisabledComponents? Can't find it in the vew versions
Actually, I even need the opposite. I have the following config:
.with_replication_config(ComponentReplicationConfig {
disable: true,
..default()
});
and I want to be able to enable replication for a specific component per entity. Is it possible in the latest lightyear version?
ah, I think I found it: ComponentReplicationOverrides
Yep that's it!
is it possible in theory to reuse the web-transport connection for another stream.
and could that stream be accessed in realtime (outside the main bevy loop)
I am thinking about how to implement voice chat.
my intution is that 1 is possible (but perhaps not in a way lightyear exposes), and that 2 is harder
What do you mean by stream? A quic stream?
yes
Well i simply spawn an entity that has a xwt_webtransport::Connection component using https://github.com/aecsocket/aeronet/blob/main/crates/aeronet_webtransport/src/session.rs; I think you could use that to add other quic streams
How do i manually update an ActionState and have it replicate that change?
When i try to manually update an axis value, it gets reset to 0
Did you make the change in the correct system set? I think it's something like ClientInputs::WriteInputs
Ok i tried adding it to the InputSet::WriteClientInputs systemset, same issue
It says the user should emit InputEvents, but i cant seem to find such a thing in the docs?
(sorry, also forgot to mention, using leafwing)
This example uses leafwing and manually updates the ActionState
Hmm, seems like it only works in Fixed(Pre)Update
Is it possible to do in the Update schedule?
It's for an orbit cam, so yeah this is required for my use case
(Or should i maybe send these updates to be applied in a FixedUpdate system?)
I'm not on a laptop right now, but AFAIK this won't be possible. Leafwing updates the action-state in PreUpdate, and lightyear populates the InputBuffer in FixedUpdate. The message is then sent in PostUpdate.
You could manually update the buffer in Update, but that seems dangerous.
What is your usecase exactly? You want the server to have a replicated cam of the client's POV?
Yeah I would just apply the updates in FixedUpdate (for replication), and then also do the updates in Update (so that the camera is smooth on the client)
I'm just trying to rotate the character whenever the orbit camera's yaw changes
Yea I would do that in fixedupdate
@pine cape why is a Predicted entity a requirement for BEI? I was hoping to use BEI with deterministic replication but because the entities aren't predicted, the ActionWraper/InputMarkers aren't setup on the replicated clients
It's not a requirement; that's Option<Predicted>
Those components should be added here for non-predicted entities: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_inputs_bei/src/setup.rs#L94
But yeah i might have to also handle DeterministicPredicted and PrePredicted
haven't had the opportunity to test all situations
probably you didn't have a ReplicationSender on your client, and ReplicationReceiver on your server
that's unfortunately required if using the new BEI
since the client needs to replicate the Actions entities to the server
I think you can also just spawn the Actions entities on the server and replicate them to the client, if you want to avoid that
I have a ReplicationSender and ReplicationReceiver on both the client and the server and I'm replicating the input component. For some reason the client doesn't get the input setup
So the setup is working right (it was how I was spawning the local players) but the input doesn't seem to be mapped to the local entities. Still trying to figure out where that mapping happens
The context entity is spawned on the server and replicated to the client?
So right now I'm trying to get projectiles to not be networked once the server replicates the projectile to the client, so on the server I use ComponentReplicationOverrides to set replication once to true, and on the client I add DeterministicPredicted and DisableRollbacks. This seems to work but there is this bug where when you fire a projectile all other projectiles jump forward a little bit, its not bad in this video but can get worst with real time connections. Anyone have any thoughts on what is causing it?
Is websocket gone?
I'm sure it's still there!
I fixed this with a recent PR request, hopefully it makes sense peri:)
Yep I haven't ported it for now. Does webtransport not work for you?
The reason is because you added DisableRollbacks. This completely excludes the projectiles from rollback, so whenever you have a rollback, the projectiles keep moving forward during the rollback frame.
When you replicate a projectile it triggers a rollback so your other projectiles move forward.
You can:
- use projectiles pre-spawning so that no rollback is triggered for new projectiles
- remove DisableRollbacks on spawned projectiles
I was thinking about using bevy as a server backend to interface with a godot game lol
@pine cape Question, what causes this? It seens lightyear is trying to map child entities when I have the component disable replicate hierarchy
DisableReplicateHierarchy simply prevents ReplicateLike from being added to the child when Replicate gets added to the parent; but maybe ReplicateLike got added by other means? I can't really help without code examples
In general it's annoying that you would need to still do rollbacks for projectiles after they were replicated. Ideally they could be completely excluded from rollbacks, meaning that their systems don't run during the RollbackSchedule.
I think there are several ways to do this:
- find a way to only run systems for
Predictedentities during rollback. Maybe a separateWorldthat only holds predicted entities? - maybe a disabled marker on entities that we don't want to rollback. We would add the marker before the Rollback schedule runs, and remove it afterwards
i can add it soon, it's very similar to webtransport
To have bei inputs synced i need to client replicate the actions? (Create receiver and sender)?
removing DisableRollbacks causes the projectile physics components to be deleted on rollback, so the projectiles just freeze. So if I'm understanding correctly DisableRollbacks just disables an entities ability to cause a rollback not if a entity experiences a rollback?
Yes
DeterministicPredicted disables the ability to cause a rollback, DisableRollback removes the ability to experience a rollback
but when you rerun the entire FixedUpdate set for rollbacks don't all entities experience it?
added websocket: https://github.com/cBournhonesque/lightyear/pull/1167
- DeterministicPredicted: we don't check for rollbacks on that entity
- DisableRollback: we don't rollback to the previous confirmed state, but the entity will still be affected by re-running FixedUpdate
What we're missing is a way to have entities be completely ignored during rollback, so that in your case your projectiles wouldn't need to get rolled back at all, and wouldn't advance during rollbacks.
You can still achieve this by only having DeterministicPredicted, but it's more expensive
so something in the DeterministicPredicted is deleted the physics of my projectiles
Any ideas on how I can buffer a client entity information in parallel with the input line? SImilar to what you did in leafwing with you fps (rotation) examples, but in BEI
We used to do it via ActionMock but it seens that does not get replicated anymore
Does it happen with Predicted as well?
I think you can directly update the ActionState, ActionTime, ActionValue, ActionEvents components and that will be buffered and replicated to the server.
Normally ActionMock should modify these components, so it should achieve what you want
I will try that, but fyi Mock is not working not idea why.
I will try to make a reproducible example
fn buffer_rotation(
q_camera: Query<(&Transform, &CameraMode)>,
mut players: Query<Entity, With<Action<RotateInput>>>,
mut commands: Commands,
) {
let (transform, camera_mode) = q_camera.single().expect("Camera to have a transform");
// Only do this if in tps mode
if camera_mode.ne(&CameraMode::Tps) {
return;
}
let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
for player in players.iter_mut() {
// This creates a pseudo-action, that continouslys gets fired every state
let action_mock =
ActionMock::new(ActionState::Fired, Vec2::new(yaw, pitch), MockSpan::Manual);
commands.entity(player).insert(action_mock);
}
}```
app.add_systems(
FixedPreUpdate,
buffer_rotation
.before(InputSet::BufferClientInputs)
.after(EnhancedInputSet::Update),
);```
perhaps i need to change the context entity?
Try to enable debug or trace lightyear_inputs::client=debug logs and check if the InputBuffers gets updated with the values you expect
I keep receiveing a log message like this one
2025-08-17T14:23:23.270473Z DEBUG lightyear_inputs::client: sending input message for "psycho_core::player::PlayerInputs": InputMessage { interpolation_delay: None, end_tick: Tick(2544), inputs: [PerTargetData { target: ActionEntity(272v4#17179869456), states: BEIStateSequence { states: [Input(ActionsMessage { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }), SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } }, PerTargetData { target: ActionEntity(371v2#8589934963), states: BEIStateSequence { states: [Input(ActionsMessage { state: None, value: Bool(false), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }
Are thos the buffered entities?
Basically you keep sending
{ state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }
Maybe something in lightyear is overriding the mocked value.
Do you have input delay?
Can you enable bevy_enhanced_input=trace logs and check for this https://github.com/simgine/bevy_enhanced_input/blob/master/src/context.rs#L438
No I do not (i setted to 0)
@pine cape 2025-08-17T14:33:26.960139Z TRACE bevy_enhanced_input::context: updating psycho_core::player::RotateInput from Mut(ActionMock { state: Fired, value: Axis2D(Vec2(1.0209994, -0.31257823)), span: Manual, enabled: true }) seens to be running fine
Directly changing ActionValue on the action entity also did not work
@pine cape Found out what it is, I need to bind it to something
If the action is just empty it wont send the message
So
(Action::<RotateInput>::new(),ActionMock::new(ActionState::Fired, Vec2::ZERO, MockSpan::Manual)), this wont work
(Action::<RotateInput>::new(),ActionMock::new(ActionState::Fired, Vec2::ZERO, MockSpan::Manual),Bindings::spawn((
Spawn((Binding::mouse_motion(), Scale::splat(0.1), Negate::all())),
Axial::right_stick().with((Scale::splat(2.0), Negate::x())),
)),), this will
my guess is you require a component associated to Bindings
Oh; you can also just add an InputMarker component on the entity I believe
I'm having trouble getting a message with an entity to work.
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, MapEntities, Reflect)]
pub struct RequestTarget(#[entities] pub Entity);
app.add_channel::<Channel1>(ChannelSettings {
mode: ChannelMode::OrderedReliable(ReliableSettings::default()),
..default()
}).add_direction(NetworkDirection::Bidirectional);
app.add_message::<RequestTarget>()
.add_map_entities()
.add_direction(NetworkDirection::Bidirectional);
On the client:
if let EWButton::Select(i) = ew_button {
let Ok(ew_pointer) = q_pointer.get(child_of.0) else {
return
};
let Some(ew_entity) = ew_pointer.0 else {
return
};
request_spawn_sender.send::<Channel1>(RequestTarget(ew_entity));
info!("Selected target {}", ew_entity);
}
On the server
pub fn handle_request_target(
mut receiver: Query<(Entity, &RemoteId, &mut MessageReceiver<RequestTarget>)>,
mut q_character: Query<&mut Target>,
peer_metadata: Res<PeerMetadata>,
q_name: Query<&Name>,
q_controlled: Query<(Entity, &ControlledBy), With<CharacterMarker>>,
) {
for (client_entity, remote_id, mut message_receiver) in receiver.iter_mut() {
message_receiver.receive().for_each(|message| {
let conn_entity = peer_metadata.mapping.get(&remote_id.0).unwrap();
// Find the controlled character
let controlled_entity = q_controlled
.iter()
.find(|(_, controlled_by)| controlled_by.owner == *conn_entity)
.map(|(entity, _)| entity);
if let Some(controlled) = controlled_entity {
let mut target = q_character.get_mut(controlled).unwrap();
info!("Setting target entity: {}, for: {}", message.0, controlled);
target.0 = Some(message.0);
}
})
}
}
Entity 197v1 is a predicted entity.
entity mapping for messages is only done automatically between the Replicating and Replicated entity.
In your case you will have to first map from the Predicted to the Replicated entity manually on the client side:
fn send_with_map(
component_registry: Res<ComponentRegistry>,
manager: Single<&PredictionManager>,
) {
// map from predicted to replicated
let _ = manager.map_entities(&mut component, component_registry.as_ref());
}
I tried doing it manually and got this:
thread 'Compute Task Pool (3)' panicked at workspace/deep_field/src/renderer/ui/early_warning_system.rs:234:141:
called `Result::unwrap()` on an `Err` value: MissingSerializationFns
I'm trying to do a Message not a Component. I passed the confirmed entity instead of the predicted one and it worked.
yeah, it seams that only the confirmed entity is mapped between server and client
since there is not 2 distinct entities on the server
I just had a playtest using a host server with 8 players, and it went super smooth, steam peer-to-peer seems to only handle 3-4 players, do ya'll think steam peer-to-peer is designed to handle more than 3-4 players?
It can handle more than that for sure. Did you try with 8 players and no steam to compare?
yeah we tried 8 players with steam p2p and it was poop, then we switched to a hosted server and it was very smooth, so I'm thinking its steam and not the game
but i don't know anything about how p2p works vs udp
Steam relay also uses udp
I'm not too familiar with the internals, but maybe some configuration is messing things up
@pine cape Why doesnt lightyear utilize the native interpolation library in avian?
Mostly because I published my version first, + mine is more general purpose, it's not only for Transform/Position
The avian one has HermiteEasing which seems interesting
Question my camera follows a player, it there any system set I should be worried about? For example: Follow cam after, frame interpolation?
Yes, after frame interpolation
Does frame interpolation run after PhysicsSet::Sync?
PhysicsSet::Sync runs in FixedPostUpdate
The latest system is TransformPropagate so I would order against that
You can see the order I use in lightyear_avian
The description of frame interpolate is a little confusing, why should I use it? Is it for predicted all scenarios?
Or any scenario whatsoever?
Because the moment I remove it, everything looks smoother on my side
it's to smooth components that are updated in FixedUpdate (like Position, Rotation, etc.) so that they get interpolated in the Update schedule
how are you using FrameInterpolation?
Currently I think using FrameInterpolation on Transform doesn't work, because of the way it interacts with Correction
you would have to use FrameInterpolation on Position/Rotation
I do use it on position and rotation, yet everything seens stuttery the moment I add it.
@pine cape I ran into an interesting bug the moment I add my lag compensation history to my player. He teleports to what I think it is the MAX value of f32. Here is the log.
I have no idea what is causing it, because in your logic it seens you create a child entity and keeps recreating the collider according to it
It seens the logic is making my collider extra chonki
Okay I will refactor the lightyear s lag comp to check if I can get my collision information . @pine cape is that okay by you instead of relying on spatial query casts make it so we check colliding colliders history and adjust as needed
I am gonna make a draft to explain what I mean
not sure what you mean, you can open an issue
If I fire a bullet that is a ball it hits a player head, and I want to get the collision details force of impact and so on how would I do that with the current api?
There is also this annoying bug but i think it should be an easy fiix
@pine cape My github isn't working for some reason rn. I tried to replicated the steam not working in headless and you're examples work with p2p connections, so you can close the issue. Idk whats going on there
maybe it has something to do with making the client entity before setup?
On the server, do you trigger on Connected vs on Linked? It's important to trigger on Linked
i trigger clientOf to add the replicationsender, and connected to add the players
should connected be switched to Linked?
Just do what I do in the example
@pine cape Issue opened will work in this PR on saturday if you give the go ahead
Sg for point 1 and 2, but I'd like to keep the code in lightyear_avian
the lag compensation problem is mostly due to teleportation, what should be done when the server teleports an entity? clear the history?
Yeah there probably should be some special case if the change of position is too big
added a video to the issue
Ought of curiosity what ide you use and how do you usually develop in lightyear?
I use RustRover, works well
hi @pine cape , after stopping my WebTransport server (using the Stop event), I want to be able to stop the underlying IO too. I have an observer that triggers Unlink when the server reaches Stopped state, and I see the following logging:
2025-08-20T17:12:49.115135Z TRACE lightyear_connection::server: Stopped added: removing Started/Starting 2025-08-20T17:12:49.115194Z TRACE lightyear_aeronet: Unlink triggered on Link entity 164v2#8589934756 (reason: ""). Closing/Disconnecting AeronetLink entity 168v1#4294967464 2025-08-20T17:12:49.115257Z TRACE lightyear_aeronet::server: AeronetServer closed for 164v2#8589934756. Adding unlinked on Server
...however I can see my application is still listening on the port number I used for the server, which prevents the application from starting a new server with the same port number later on ("Address already in use (os error 98)").
Is this expected? (Is there currently a reliable way to fully pull down the server without restarting the application?)
Many thanks!
Thanks for the report, this is not expected! Unlink should completely shut down the underlying IO
@pine cape I am making it so lag compensation also handles multiple child colliders
2025-08-20T19:44:44.052757Z DEBUG psycho_lag_compensation: collider=ColliderAabb { min: Vec3(inf, inf, inf), max: Vec3(-inf, -inf, -inf) } @pine cape Found out the cause of the chonki colliders, avian for a single frame returns a infinity collider aabb
@pine cape There is two impls of the same thing in history buffer
one is for &HistoryBuffer
oh silly me
hi folks! I've just published a draft of my first Bevy blog post, and it happens to cover some stuff related to lightyear, such as replication and delta compression
if anyone would like to do a technical proofread or just leave some general feedback, I'd be super grateful:
https://vladbat00.github.io/blog/000-spawning-entities/
Making client and server spawn entities in "co-op". Patterns and lightyear replication.
Are you developing a game in bevy Masterr Vlad?
How did you guess my university degree? 😅
Yes, I am. Not quite ready to show it yet, but hopefully will be soon
Lightyear has been a huge help so far
The post seems great to me!
The factory approach is interesting.
And for delta-compression you are very right to mention that this wouldn't work for components where the diff is expensive to compute.
I actually had an issue open: https://github.com/cBournhonesque/lightyear/issues/1045 where I wanted to implement a separate kind of DeltaCompression where instead of computing the diff manually between 2 component values, the user specifies the diffs that should be applied
I guess it would be nice to see a picture/video of the result
Thank you, those were just the things I was unsure of 🙂
Oh this is great, I think I'll link the issue as a note that it may get addressed in the future versions
I'm noticing occasional desync from the server on the interpolated clients with BEI. It seems to get corrected once I start moving again which leaves me to believe maybe it's packet loss or something, but it made me wonder if it will resend unacked replications on the interval even without any changes or if there's a way for me to do that?
It could be something else entirely, but it made me realize I don't fully know how it behaves
You observer this when testing locally or in real conditions? Packet loss is normally very small.
But yes we resend replication packets for Insertions/Removals/etc. to make sure they are acked, but not for Updates, so it's possible that the client didn't receive the last packet and is temporarily stuck in a different state than the server.
The logic is here: https://github.com/cBournhonesque/lightyear/blob/cbcb42825de980430a12dfa56c49f34faea03473/lightyear_interpolation/src/interpolate.rs#L133 but probably needs to be refactored, the current implementation is a bit confusing.
Basically the interpolation timeline progress smoothly (tick keeps increasing), and we record the ticks at which we received server updates.
Then we compute the fraction between two consecutive start_tick and end_tick that we are interpolating between.
Normally we should have been interpolating all the way to the last received update.
@pine cape Rough draft of what the lag comensatio api will look like
I am curious thos, what is the point of that slerp lerp in the ray cast? If a player is hit you want us to return to where he was in server?
Only in testing so far when using a conditioner or messing with the send_interval to see what values work, but the part of it never being corrected without another action resulting in an update seemed like something I might want to address for my own game
Are send_intervals closer to the tick rate problematic?
Digging through sender is clearing up a lot of my questions I think
I guess I basically want ChannelMode::UnorderedReliable with different ReliableSettings
Or maybe I'm thinking about it wrong and I should just do periodic updates or something 😅
The part where you have an update A on server that you never interpolate towards on the client unless you send a new update B seems like a bug. It would be super helpful if you could open an issue with a small example.
It's to interpolate between 2 ticks when we check from the client's POV if there was a hit
I think sending updates as Unreliable is fine; the sender is probably not the issue if you can reproduce this with 0 packet loss
I think I've just realised one misconception I had about PredictionMode::Once. It doesn't prevent continuous replication of a component to Confirmed entity, right? (i.e. meaning this mode won't be affecting the network traffic)
It only affects the local behaviour of clients, so that the component data just won't be copied from Confirmed entities to Predicted ones, right?
to save on the network traffic, I actually need ComponentReplicationConfig::replicate_once
for some reason I assumed that if both interpolation and prediction is set to Once (or disabled), it would also affect replication itself
Correct
Is it possible to predict other player's inputs with BEI? I'm using rebroadcast_inputs, but I think I need to set up replication for the actions manually?
Actions are replicated to the server, but it doesn't look like actions get replicated back down to other players.
I haven't tested rebroadcasting with BEI, I'm in the process of setting an example to try it.
I think it should work, maybe you're having an issue with entity mapping?
Yeah it's entity mapping errors on the client with IDs of the host's actions. Those actions don't exist on the client. I'm not at my PC now, but I'll copy the errors here when I get back.
The context component was properly replicated to the predicted entity BTW.
2025-08-23T17:12:42.626906Z ERROR lightyear_serde::entity_map: Failed to map entity 341v2#8589934933
2025-08-23T17:12:42.626932Z ERROR lightyear_serde::entity_map: Failed to map entity 330v2#8589934922
2025-08-23T17:12:42.626938Z ERROR lightyear_serde::entity_map: Failed to map entity 327v2#8589934919
2025-08-23T17:12:42.627006Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequ
ence { states: [Input(ActionsMessage { state: None, value: Axis2D(Vec2(0.0, -0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEv
ents(0) }), SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPre
cedent] } end_tick=Tick(48554)
2025-08-23T17:12:42.627029Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequ
ence { states: [Input(ActionsMessage { state: None, value: Bool(false), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }),
SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } en
d_tick=Tick(48554)
2025-08-23T17:12:42.627039Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequ
ence { states: [Input(ActionsMessage { state: None, value: Bool(false), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }),
SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } en
d_tick=Tick(48554)
The only major difference between this version and the interpolated one is the addition of rebroadcast_inputs, is there something else I should be adding to the replicated entity or protocol differences?
Ah yes I guess it makes sense that rebroadcast doesn't work because the Action entities of client 1 are not replicated to client 2. You would have to manually add Replicate on the action entities on the server to make sure they are rebroadcasted to other clients. Maybe this can be done automatically by adding HierarchySendPlugin<ActionOf> (so that the Replicate gets propagated automatically from the Context entity to the Action entity on the server), but it needs to be tested
Randomly unable to build with wasm clientside
error[E0599]: no function or associated item named `builder` found for struct `lightyear::prelude::client::ClientConfig` in the current scope
--> modules/core/networking/src/network_client.rs:51:43
|
51 | config: ClientConfig::builder().with_no_cert_validation(),
| ^^^^^^^ function or associated item not found in `lightyear::prelude::client::ClientConfig`
building non-wasm works fine lol
It seems like HierarchySendPlugin<ActionOf<...>> replicates other client's action entities, but not the HostClient. Also, the predicted copy of the other client doesn't have an Actions component.
Experiencing this in Firefox 142 against localhost with WebTransport as well (chrome works): https://github.com/cBournhonesque/lightyear/issues/251
According to https://caniuse.com/webtransport, FF 124 is supported? On Firefox 124, MacOS (Silicon), I get a vague WebTransport connection rejected error. I tried connecting from [::1]:8080, localh...
Can you post this in the aeronet channel? I'm using aeronet_webtransport and they would probably know more
fwiw, I'm having this same issue. I haven't found a good solution for it yet
yep i'm working on an example for projectiles replication using BEI; i will have to rebroadcast BEI inputs for it so i'll be looking into that
FYI another issue I had with the BEI refactor was that I was manually setting an input for leafwing like in the FPS example to sync the camera position and that didn't immediatly work for me.
The issue is that I wasn't doing any bindings to the Action, so lightyear didn't add the InputMarker component
The fix was just to add the InputMarker component manually to the Action. This is our Action:
context.spawn((
Action::<CameraForwardAction>::new(),
LocalActionComponent,
// no bindings since this is mocked
// Added to help lightyear pick up this action since it doesn't have bindings
InputMarker::<NetworkInputComponent>::default(),
));
Also if you're wondering the BEI equivalent of set_axis_pair and friends from leafwing is, it's done through mocking. Here's the system we use:
fn third_person_camera_input_system(
transform: Single<&Transform, With<ThirdPersonCameraComponent>>,
action: Single<Entity, (With<Action<CameraForwardAction>>, With<LocalActionComponent>)>,
mut commands: Commands,
) {
commands.entity(*action).insert(
ActionMock::new(ActionState::Fired, transform.forward().as_vec3(), MockSpan::Manual)
);
}
I also recommend setting networked actions and local only actions with different context components. Otherwise I saw a lot of warnings. I'm assuming it was trying to replicate the whole relationship, and not just the actions that are defined in the protocol.
Both contexts can exist on the same entity. We literally attach them at the same time.
fn add_local_input_observer(
trigger: Trigger<OnAdd, LocalInputComponent>,
mut commands: Commands,
) {
commands.entity(trigger.target()).insert((
Actions::<NetworkInputComponent>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
// Networked actions
})),
Actions::<LocalInputComponent>::spawn(SpawnWith(|context: &mut ActionSpawner<_>| {
// Local actions
})),
));
}
Yes I ran into this as well, I'll probably also add the InputMarker automatically if ActionMock is added on the action entity.
Another thing I need is to be able to have actions that are unaffected by rollback (for example global actions like pausing the game). I did it right now by adding DisableRollback on the Action entity, but I think I also want to allow users to add BEI Contexts that are evaluated in PreUpdate
Does anyone know why RustRover is not able to find Tick? It doesn't work through lightyear::prelude, and importing lightyear_core directly does not work either. I already have org.rust.macros.proc enabled and Expand Macros enabled in the rust settings.
Guessing that is related to it being created by a macro?
I tested the deterministic_replication example and noticed that it is very easy to become out of sync, is there any good solution to correct the state at some points?
I also made both the client and server crash quite a few times because of some panic unwrap calls.
If that's the case, @stray sinew the notes I have from using Rider a couple years ago may still be relevant for you:
type
Experimental featurein the dialog of the Help | Find Actionenable the features org.rust.cargo.evaluate.build.scripts and org.rust.macros.proc
Thanks, I already have those enabled
Still not working, I'll try vscode next
My b, I noticed the end of your message 
I am sent to this line in vscode.
https://github.com/cBournhonesque/lightyear/blob/main/lightyear_core/src/tick.rs#L10
Which rust version are you using?
1.89.0
Yep, no issue with VScode. Seems to be a RustRover specific issue
what kind of panics are you getting?
It works for me with RustRover
It was working for me too like last week, and then it stopped working on both of my machines? Not sure what caused it.
Maybe try File -> InvalidateCaches
I already tried that and it didn't work. I suspect its related to nightly builds because the issue showed up after I switched from the nightly compiler to 1.89 but I'm not sure since I had a lot of other changes too
For some reason the examples don't work if the server is spawned with only the server and netcode features; the client cannot connect.
It works if the server also has the client feature; can't figure out why
I just tried the examples for 3 minutes with 2 clients and was unable to make it go out of sync. Would you mind opening an issue with more details?
There seems to be a bug when updating ChildOf with PredictionMode::Simple.
On the server:
commands.entity(character_entity).insert((
ChildOf(parent_parent_entity),
));
// Registered like this on both
app.register_component::<ChildOf>()
.add_immutable_prediction(PredictionMode::Simple);
Crash on the client:
thread 'Compute Task Pool (0)' panicked at /Users/rherv/.cargo/git/checkouts/lightyear-16a1ca81dacb5ed5/82026f6/lightyear_prediction/src/predicted_history.rs:133:13:
assertion `left == right` failed
left: Once
right: Simple
stack backtrace:
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `lightyear_prediction::predicted_history::apply_immutable_confirmed_update<bevy_ecs::hierarchy::ChildOf>`!
Is it because of this? How would I override the plugin or the registration?
https://github.com/cBournhonesque/lightyear/blob/17b40188a39e36c87c6680071a21bf7776c79305/lightyear_prediction/src/shared.rs#L19
hm yes it's probably this
i can update them to Simple for now; as it seems more flexible
I have a high chance of putting it out of sync while jiggling inputs (A/D keys) when getting close to the ball.
I am not able to replicate it now, will save all the future ones I hit
@lime jungle for #networking message:
Lightyear works with 2 layers:
- IO for how to send bytes (crossbeam, udp, websocket, webtransport). This is the
Linked,Linking,Unlinkedcomponents - 'connection' layer for auth + establishing a 'session' (a more long-term ID on top of the raw link)
I provide 2 implementations of the connection layer, which both give you a long-term id: Steam or Netcode.
But you're free to implement your own, the only requirements are that:
- you handle associating IDs (
LocalIdandRemoteId) components on the Link entity - you handle the connection process by adding the
Connected,Disconnectedcomponents as necessary and handling theConnect/Disconnecttrigger. (for example like so: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_steam/src/client.rs#L103) - on the server, you handle adding the
Started/Stoppedcomponents, and handling theStart/Stoptrigger.
Let me know if that makes sense or if you need help with that
it seems i will be accepting byte messages over websocket, feed those into crossbeam channels and then implement the connection layer myself. i will look into it and then reply to your message, thanks!
@pine cape Does the replicatelike component also follows up adjustements in rooms?
Yes it should
That was actually the main reason why I introduced ReplicateLike (to avoid copying the room information on children)
@pine cape To replciate somewhat a relationship all I need to do is implement entitymapper on both target and targeted?
you can just add HierarchySendPlugin<Relationship> and you just need MapEntities on the relationship component
(ChildOf and not Children)
Has anyone made a stable version with lightyear and bevy-tnua?
I don't think so
I have a fully function char controller but not tnua just floating character
Thank you Master Peri
@earnest fog @unique plover it's not cleaned up but I think you should be able to use branch cb/projectile-demo-interpolation-sync for BEI input rebroadcast
Do you replicate the full state for all characters?
I might be off track, but I was wondering if it would be nice to skip rotation for all characters and skip linear velocity for local characters.
I was thinking of trying optimistic prediction on the local characters with server authoritative position. I am not happy with the results, and quite unsure exactly how I want it to work smoothly.
Ahmm are you frameingerpolating him?
@pine cape Correct I am mistaken but the hierarchy replication is in summary a handy way of ensuring that a related entity follows the replication logic from root. It does that how tho? Cant seem to find the logic of the ReplicateLike component (only the insertion and remotion logic) on the plugin
And in my case scenario which is the following:
I have a root entity, with related entities. I just need the information of those releated entities to be replicated (no need for interpolation or prediction). It also needs to follow the NetworkVisiblity of the root entity. So I was thinking of doing the following, add ReplicateLike to related entities. Remove the prediction and interpolation component and be cheerios. Would that work?
Also this wouldnt work, because well R is not necessarily ChildOf
You’re incredible. I’ll give it a try tonight. Thank you!
ReplicateLike is use in the core replication systems in lightyear_replication::send::buffer.rs
I actually needed that recently in my branch cb/projectile-demo-interpolation-sync
You can do this: https://github.com/cBournhonesque/lightyear/pull/1183/files#diff-3f085b5b6de1bf196399b8bb71ace7922da59752295a4afd5e4620a777ebeaa6R143
ReplicateLike uses the same replication behaviour of the root entity, but you can still override the replication components to customize the replication
thanks for the catch
Interesting another thing that worked was just listening to whenever root entity network visibility got changed/added
although this is way better
yeah, moving is okay, but jumping is very laggy. I am going to leave it like this for now, and revisit it later as I can continue on other parts for now.
Probably having rollbacks on jumping
yeah probably rollbacks that are mispredicted on jumping
I tried to turn off that and it just works slightly better
@unkempt sedge did you make progress on your lag compensation code?
@pine cape I finished it but i did not handle the slerp scenarios yet
I still need to handle the diagonal movmemnet too
I've got rebroadcasted inputs for BEI working + i have a bunch of fixes on correction/prediction, so i was thinking of making a new release
shouldn't the implementation be independent from movement direction?
well dashing diagonally for some reason is kinda bad
@pine cape FYI the cb/projectile-demo-interpolation-sync branch has a dependency of ../bevy_enhanced_input so I'm unable to test it with my setup.
I mean I could, but I would have to change the scope of my nix flake.
yes.. unfortunately it's required; it's this PR: https://github.com/simgine/bevy_enhanced_input/pull/189
if you want to download and use that branch locally
Cool, I think I'll wait unitl that's merged then. Thanks!
@pine cape A weapon with a state machine should be predicted correct?
I'm experiment with weapon/projectile replication rn
I think weapon should stay responsive so you should predict it, yes
For example: Clip ammo should be fully synced or just simple synced? Dont know if simple sync can couse rollbacks
It should use full prediction
ya I imagine firing and reloading can be predicted, just probably wouldn't predict damage/death for anything hit. Some of the hit vfx and sfx are probably good to predict to help smooth over the delay before confirmation although can sometimes be misleading in a rollback
I made a PR: https://github.com/cBournhonesque/lightyear/pull/1190/files
I don't know if it's me or not, but main is failing, and ☝️ is also failing with the same errors, but not any new ones. So hopefully it's ok. I haven't smoke tested against anything other than webtransport.
is it possible to connect a custom io layer to lightyear? if so is there anywhere i could look to see how to do it
Sure you can look at existing IOs like crossbeam: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_crossbeam/src/lib.rs
You mainly need a receive and send system + reacting to events like LinkStart, Linked, Unlinked, etc.
Or udp for a client-server one: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_udp/src/lib.rs
quick question. does lightyear itself handle reliable sending or is that delegated to the io layer being used?
lightyear handles it; so the io can be unreliable
Just want to say the projectiles example looks amazing! The thoroughness is appreciated, especially as another BEI example
helps answer so many questions with the different options and the readme
Thanks! Funnily enough the readme and the example were initially entirely generated by chatGPT. I was surprised to see that the implementation worked so well.
It's still heavily WIP though, i'm working on making the examples work with 2 weapons only (hitscan and linear bullets)
Haha I assumed the readme was but ya that's surprising for the implementation
On my pr lag compensation is now a one to one relationship
It seens you also ran into the diagonal movement clunkiness
Quick peek at the new example; the goal is to have a sandbox where we can try many different ways of replicating projectile to see what 'feels' best.
I was working on a FPS project but it was very hard for me to decide on an approach for replicating projectiles because there are many different ways to do things and it's very time-consuming to test all of them. So hopefully using this we'll be able to explore the trade-offs between the various approaches.
On the right is the server, which spawns 6 differents entities for each player. We use interest management via Rooms to make sure that clients only receive updates about entities that correspond to one of the 6 modes:
- AllPredicted: entity is predicted by all players, hit detection is done server-side with no lag compensation. This should favor the target since the shooter has an imperfect view of their position. This also allows testing that remote entity prediction works well with BEI, which is now the case.
- ClientPredicted (No lag Comp): the 'default' setting of predicting the client and interpolating other players. Hit detection is done server-side; since there is no lag compensation, even if you seemingly hit the target on the client's screen it won't be registered as a hit on the server because the client has a delayed view of other clients
- ClientPredicted (Lag Comp): same as above but this time we use lag compensation on the server. The white boxes on the server are the collider occupied by each player in the last few frames. If the projectile collides with that (broad-phase), we check if there was an actual collision on the client's screen using the narrow-phase lag compensation query. It's interesting to see how the boxes grow after any diagonal movement, but I think that's expected (it's just for broad-detection)
- ClientSideHitDetection: same as above, but hit detection is done on the client. This should give good results + saves a lot of server-CPU (since server doesn't need to do any hit-detection), but cheaters are free to send fake 'I hit that target' packets
- AllInterpolated: this time the local client is also interpolated, so each of their movement will be felt after a delay. That's a tradeoff to make in exchange for having all entities in the same timeline, which removes the need for lag compensation. Note that interpolation seems somewhat clunky because we interpolate between infrequent states without having a full view of the history of each component. I'm planning of maybe adding a mode where the full history of the component is replicated, for better interpolation.
- OnlyInputsReplicated: this time the server does basically nothing except acting as a proxy that exchanges inputs between players. The simulation should be deterministic and run on each client. If there is enough input-delay (for example in lockstep), each client has a perfect view of other clients and there is 0 prediction. Otherwise the client has to predict other players, which can make things tricky. How do we handle mispredicting that a target was shot? How do we handle receiving a late input telling us that a remote client shot a bullet?
The example also uses a fake 'bot' client that acts exactly like another client but runs in a headless app and communicates with the server using crossbeam channels. This is useful for testing to be able to see how remote clients work without having to manually spawn 2 clients.
I think that's expected actually
I'll post this in #showcase ; I think this is already quite a lot of progress 🙂
It was a lot of work to even get to this stage
i dont think so a characterr walks diagonally hje has way more "collision" doesnt sound okay to me, dont know about your impl tho
mine feels clunky, we actively jump and so on he becomes too big
i am guessing is due to the cuboid shape
The broadphase collider is simply to have a cheap way to check if there was a chance that the projectile collided with the lag compensation history. So we include the biggest AABB bounding box that contained the collider during the history.
If and only if there is a match with that broadphase collider, then we check for the actual lag compensation collision with the exact position of the collider lag_compensation_delay ago
her being too big wont imply too many false positives?
it is not like it is gonna be expensive
Feel free to open an issue about benchmarking this; we could test it and see if it's worth it or not
I think I will build a ue5 like impl
https://github.com/marcohenning/ue5-server-side-rewind - Following guys like these
my implementation seems very similar to what's described in those blog posts. However i'm storing ColliderAABB instead of more complex Colliders in the LagCompensationHistory because I know nothing about animation.
We might want to handle complex colliders, yes
since avian utilizes parry beneath, it is quite easy to store the constructing shapes and reconstruct from there in a cheap way?
okay i am hyped this will be fun
New commit working great. I also switched from leafwing to BEI and its running so smoothly with no rollbacks
@pine cape Still trying to get the cb/projectile-demo-interpolation-sync branch working in my project.
I noticed when trying to launch my dedicated server instead to rule out HostClient that this like should probably be behind a client feature?
https://github.com/cBournhonesque/lightyear/blob/cb/projectile-demo-interpolation-sync/lightyear_inputs_bei/src/plugin.rs#L21
Yes probably
~~I'm also getting the error No Server found in the world From here
https://github.com/cBournhonesque/lightyear/blob/cb/projectile-demo-interpolation-sync/lightyear_replication/src/send/components.rs#L719
I don't know if that's just my DS or the branch yet though.~~
That error is very strange because I defintely get an entity with both Server and Started before that.
Also only happens on my DS, not in HostClient mode.
Nvm, it looks like that isn't related to this branch.
Still having DS troubles, but I was able to get further with HostClient. The actions for remote clients seems to work, but the HostClient's actions do not seem to get replicated.
I haven't tested with host client, I'd be surprised if it worked
I think I'm missing some HostClient/HostServer bounds in lightyear_inputs_bei/setup.rs
@pine cape Found a interesting nuisance, if you replicate an entity that has a relationship,the ordering of the relationship is not cool 🙁
So lets say relationship is a vector duel, katana, shotgun
Confirmed has duel, shotgun, katana,
but predicted has katana,shotgun,duel
Or whatever in da hell comes first
The fix: Replicate the target component to confirmed, than reformulate the relationship on the client end
Hm, that's unfortunate
We could detect if one of the replicate componend is a RelationshipTarget; in which case we could re-order the entities that are replicated to be in the smae order
I have no idea on how to do that :>
hahahhaa
besides identify via trait
For example: When do I know that the entity will be first replicated?
You can't do that by yourself, the lightyear internals need a change. I opened an issue
mind sending it to me I wan to give it a shot
fn reorder_relationship(
query: Query<(Entity, &WeaponCarrier), With<PlayerMarker>>,
weapon_slot: Query<&WeaponSlot>,
mut commands: Commands,
) {
for (carrier, weapon_carrier) in query.iter() {
let mut vec = vec![];
for (correct_slot, weapon) in weapon_carrier.iter().enumerate() {
let slot = weapon_slot.get(weapon).unwrap();
if slot.0 as usize != correct_slot {
vec.push((slot.0, weapon));
debug!("WRONG {}", slot.0);
}
}
vec.sort_by_key(|(slot, _)| *slot);
for (ordered_slot, entity) in vec.into_iter() {
commands.entity(entity).insert(WeaponOf(carrier));
}
}
}
I dont want this code on me prettyy api
my goodness rollback logs now are pretty chonky and prettyy
Hey there,
I used the space ship example and have added a static rigid body circle obstacle.
Does somebody know why the client ship clips into the obstacle when colliding?
The collider exists on the client and on the server(rendering is done using the collider shape like in the space ship example)
I suspect that there might be some kind of prediction/collision setting I am missing.
You can see the issue in the video.
I am very grateful for any help.
That probably means that the collision is not being predicted.
You are predicting that you are passing through the obstacle, but the server sends a correction about the collision.
Did you add the collider components on the Predicted entity?
Yes I do but now I see that I forgot to add the static rigidbody.😆
(Yup it works when I add it.)
Thanks for the quick response😄
Heya!
I'm currently trying to grok lightyear, and I am making progress. Currently I'm trying to see how I could achieve the following:
- n players, each with m cards in their hands
I have a hierarchy of [Player] -> [Hand] -> m x [Card]
Each Card has components that specify what the Card does etc... Now, I would like to replicate the entities, but not their components, unless they are the given Player's Hand. Basically so you can't see what others have.
I already found NetworkVisibility, but that seems to be wholly on/off. Is there a way to disable component replication without disabling entity replication for specific clients?
Yes, look up ComponentReplicationOverride! You can also grep it in the examples
You can also in the protocol set the ReplicationConfig to not replicate the component by default, in which case you would use the ComponentReplicationOverride to enable the component (instead of disable)
Okay, so I'd add this to every entity I don't want to replicate, as the default is to replicate?
Does this follow the hierarchy? So I could just set it in the Hand once for example 🤔 (There is no mechanic of looking at other peoples hands)
The default is to replicate, in which case you would do something like ComponentReplicationOverride::default().disable_all()
But you can also register the component as being disabled by default (.with_replication_config) in which case you would use the override to enable replication
Yes it follows the hierarchy!
Alright, awesome! Thank you for the infos :3
@pine cape My crate has a implementation of lag compensation pretty much done here is the link for it advantages when campared to your.
- It is a little more precise (it listens to collision event of where the would be of the collider)
- It support multiplo colliders/hitboxes.
- It is very easily capable of handling animation
If you want to have a test drive I will link to ya, if not i can convert and open the pr
can you open a PR? I'm on holiday for 10 days but I can take a look after
holyday? Hmmm i wonder mr peri where are u from?
you know mr peri we have a picture of you in our wall of images, yet we know so little about our lord and saviour
I'm from France!
I'm having an issue with Lightyear I think is an issue of the crate.
I get consistent RTT / Jitter sitting idle in my game, on web or native:
As soon as I start to move (use inputs) the RTT skyrockets on web, and becomes unplayable and goes into a death spiral @pine cape
I remember having a very similar issue a while back, and the solution was (I think) to reduce the input replication rate on web. I am using bevy_enhanced_inputs.
Is this still the recommended approach? It really feels like something is fundamentally wrong, idk.
What are you using for physics?
Sounds similar to numerous rollback loop errors I’ve had previously. Sometimes a poorly coded system wont fully resolve a client server mismatch with a rollback. Using delta time will sometimes cause this and relying on predicted components getting inserted and removed can trigger it too.
avian2d
I am basically adding a physics bundle (LinearVelocity, Position, Rotation) to each player in the screenshot. When players use inputs, this adds speed to the LinearVelocity component.
I've noticed many weird behaviors, and really cannot for the life of me pinpoint exactly wtf is going on, it's so frustrating.
- Sometimes my player inputs get stuck (I press things and the player stutters momentarily).
- On web I get a lag spike when the RTT spikes when inputs are sent by the local player (as shown in the screenshot)
- Sometimes the player does an initial stutter in location when the first physics interactions happens, or when I start moving the first time.
I am disabling the SyncPlugin, SleepingPlugin, and PhysicsInterpolationPlugin from the PhysicsPlugins by avian2d since lightyear is supposed to handle those things.
This is what I mean by input just randomly locks up. The terminal is the server printing whenever it receives a Trigger<PlayerMovement>. The server is seeing constant inputs from the broken client, while the other works just fine. It's super inconsistent, happens 20% of the time, and only witnessed when there are multiple clients connected.
update: it seems that input is some type of system ordering issue.
If I close the bad client and open it again, there's a chance the new client works fine, able to send inputs to the server.
oohh you know what I see that every now and then too
After some testing I was never able reproduce the error on my Windows PC running at 165FPS but on my Mac laptop on 60fps the error happened about 50% of the time seemingly randomly. Here's a video of it happening (WASD in my game controls the ship's rotation):
Is the server headless? I noticed that there are issues caused by running the server with a GUI. I would test with the server running in headless mode
It is possible there is some unexpected input bug with multiple clients
The server is headless and there is only 1 client connected
I encounter this and my server is headless and indeed I am using MacOS as well
@stray sinew Your game looks super cool, too!
Do you also encounter this in the examples? The inputs_bei example or the projectile example
Is there an example that uses the combination of BEI + Avian2d?
@stray sinew is your network tick rate the same as your replication rate? I am using 10Hz for both and wondering if there's some type of timing issue
I think the inputs are sent on the replication interval, right? @pine cape
Inputs are sent every frame
Yes, projectiles
It's still kind of a wip example, but it should be enough to test the input issue.
That sounds problematic though, doesn't it? What if someone has higher FPS than another client?
I'll try it out.
It's just to send inputs as quickly as possible since they are very time sensitive.
If someone has a higher FPS, they would send a lot of inputs for the same tick. It's not ideal, but not a huge issue
I believe you can override the replication frequency of the InputChannel if you want to change this
By the way, I printed out whenever input is triggered when this lockup happens. The server never gets input packets but the client is saying it is sending to the server. I don't understand why the client stutters position though, if it's a Predicted entity. It's not getting rolled back either, since I print out when a rollback occurs. Is there a system that cancels or rollsback the input on Predicted entities to your knowledge @pine cape ?
My only guess is that there could be a system ordering bug where queueing and sending the inputs are ambiguous systems, because sometimes the server gets them for half a minute, then stops. It just happens, randomly.
--
I know you're on vacation, Periwink, but if you ever want to DM a screen share when you come back, I can reproduce this issue.
There is a system that rollbacks inputs on predicted entities but that should only happen during rollbacks.
Sure can you send me a screenshare?
But what would be more helpful is a minimum reproducible example. Can anyone reproduce this with the existing lightyear examples?
I'm also using 10HZ maybe that's related?
I haven't been able to recreate it in the BEI example.
I saw issues even using the BEI example when I'd set the send interval close to the tick rate
I'm not sure if they're the same ones you're seeing
actually pretty sure they're not, the issue I saw was just related to interpolation I think (multiple clients)
Hi, I've been working on trying to understand lightyear, particularly for 3d. Looking at the avian_3d_character example, it seems like the client prediction isn't working - inputs are reflected on the server before the client character moves. Am I missing something? The readme for that example makes it sound like client movement prediction should be enabled in that demo
Also the client movement is jittery - seems like it's moving in step with the network for controlled entities. Remote entities don't have that so ironically Client 1 looks better on Client 2's screen than on their own
Also try using the main branch from lightyear, it fixes some input related bugs
I think there is some input delay enabled in this demo, so every input is delayed on the client. (Probably so you have time to perfectly predict other clients as well)
You're talking about this right? I think its commented out on the current main branch
Yes I had an open issue about the movement not being smooth in that example, someone commented with a potential fix, maybe try that?
let me see if I can find it
@pine cape I think I found the commit you're talking about. It doesn't seem to have any impact on the issue. Take a look - you can see the server moves before the client does:
That commit does fix overcorrection mention in the issue though
just not the input lag / lack of client prediction
https://github.com/cBournhonesque/lightyear/issues/1113 this is the issue I was referencing - let me know if you were talking about a different one. ty!!
If I upgrade to main I get this panic on load:
thread 'main' panicked at /Users/simbleau/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_ecs-0.16.1/src/error/handler.rs:141:1:
Encountered an error in system `lightyear_frame_interpolation::visual_interpolation<avian2d::position::Position>`: Parameter `Res<InterpolationRegistry>` failed validation: Resource does not exist
Any ideas?
I have interpolation activate, not sure why I would get this.
app.register_component::<Position>()
.add_prediction(PredictionMode::Full)
.add_interpolation(InterpolationMode::Full)
.add_should_rollback(position_should_rollback)
.add_linear_interpolation_fn()
.add_linear_correction_fn();
That’s odd, I got my client and server to be 1:1
Due to low latency they will be pretty much really close to each other
All frames unless you simulate high ping, which I dont know if that example has
Does someone have an example of using both a crossbeam as well as netcode server + client?
I am not sure how they interact, and how things are meant to work together.
I get that Link is the 'logical' connection, into which you can feed from any source.
But looking at CrossbeamIo it says that new_pair is for testing? So I assume that for non-testing, one should assemble it oneself. But is there a specific reason?
I also assume that Client is the same thing basically?
I am not sure if it could help you, but I have been using this amazing example by SueHeir as a base for my games: https://github.com/SueHeir/lightyear-menu
It starts a server in another thread where the "host" client seems to connect through Crossbeam, while remote players can join with either Steam or UDP
Thanks! I will take a look :3
@pine cape @stray sinew I get the client input lockup on main as well, and two new issues:
Issue 1: Bad Input spam in terminal if I turn on rebroadcast_inputs: true (screenshot attached)
Issue 2: Interpolation registry missing error (I had a message above with it). Turning off visual interpolation still has the input lockup, so it is not related to visual interpolation.
Note: Tinkering with the send rates did not help. Input lockup still occurs with both homogenous and heterogenous intervals.
/// How often inputs are sent.
pub const INPUT_SEND_INTERVAL: Duration = Duration::from_millis(1000 / 60);
/// How often replication takes place.
pub const REPLICATION_SEND_INTERVAL: Duration = Duration::from_millis(1000 / 60);
/// How often the networking ticks.
pub const NETWORK_TICK_RATE: Duration = Duration::from_millis(1000 / 15);
Yeah I know, but even still you can see in the video that the server view reacts slightly before the client does. Does lightyear have a built in mechanism for simulating high ping?
Thank you for confirming this, I'll try pulling your commit directly, maybe there is something I'm missing. will follow up!
I can have a look in a few hours again
I have a video that shows 3 issues.
- Triggering input on WASM causes a death spiral
- Input lockup (we've talked about this one)
- Inputs when I'm not even pressing anything!
Sometimes, input continues to spam even when I have nothing pressed or not on the window anymore. No clue why.
As soon as I press inputs, usually either the RTT goes into a death spiral, or it locks up and refuses to work. And finally, when I am on another window, or no longer focusing the same tab, RTT recovers. There must be some correlation between inactive window focus and correctly networking. Maybe the WebKeepAlivePlugin is doing something really good?
@stray sinew have you encountered all of those 3 issues or just the input lockup?
@pale sequoia confirmed - I just tried it with the exp/avian3d branch in your repo and had the same result
Ok, i guess it is something else. I just ran the test with 100ms network lag in Clumsy and the prediction seems to work. Maybe it's just the character controller causing it to be stuttery/slightly lagged?
I'm experiencing 2 often and 3 rarely. Sometimes an input will randomly get "stuck" and won't unpress for a bit.
I find it a little bit hard to tell, but there could be some slight rollback?
For the other clients they seem very laggy. Would have been nice to see the fps here, but I believe they are all stable.
Would the rollback cause visual stutter? Interestingly I was not having lag in other clients. I was thinking an fps counter and also a visual display for inputs might help too
Did you add the lightyear plugins before adding frame interpolation?
You can also look at the tests: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_tests/src/stepper.rs#L150
to see how to manually add a crossbeam client to a netcode server
Does your input lockup issue only happen when you test using wasm? I haven't tested wasm for months and I never ran into that issue.
Also just a reminder that enabling GUI on the server slows down the server frame rate and can cause issues.
When you switch to another tab, we manually (via WebKeepAlivePlugin) call app.update every 100ms on the client. Maybe the slow send rate helps things recover.
- This is normal; currently we keep sending inputs even when you're not pressing anything. (to distinguish between inputs not being received, and empty inputs)
I just tried running the projectiles example in wasm (with branch cb/projectile-demo-interpolation-sync) and I cannot reproduce the issue.
I used rm -rf SL && cargo run --no-default-features --features=server,netcode,client -- server 2>&1 | tee SL to start the server, and
bevy run --no-default-features --features=client,netcode,gui web --open to start the client.
I can't reproduce
- death spiral if inputs are pressed
- inputs lockup (server stops receiving inputs from client)
I can't help much if we can't reproduce this in an example
Oh wait, am I getting this right, that one should not mix multiple Transport on a single entity?
I can't seem to get it to work, as when I do, it spams:
2025-09-09T16:12:58.048185Z DEBUG lightyear_netcode::client: client sending connection request packet to server
2025-09-09T16:12:58.064921Z ERROR lightyear_transport::plugin: Error processing packet: PacketError(ChannelNotFound)
My code:
pub fn start_hosted_server(trigger: Trigger<StartLobby>, mut commands: Commands) {
let StartLobby {
name: _,
socket_addr,
} = trigger.event();
let (server_conn, client_conn) = CrossbeamIo::new_pair();
let server = commands
.spawn((
NetcodeServer::new(NetcodeConfig::default()),
LocalAddr(socket_addr.parse().unwrap()),
ServerUdpIo::default(),
server_conn,
ReplicationReceiver::default(),
ReplicationSender::default(),
))
.id();
commands.trigger_targets(Start, server);
let client = commands
.spawn((
NetcodeClient::new(
lightyear::prelude::Authentication::Manual {
server_addr: std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
12312,
),
client_id: 0,
private_key: lightyear::netcode::Key::default(),
protocol_id: 0,
},
lightyear::prelude::client::NetcodeConfig::default(),
)
.unwrap(),
PeerAddr(std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
1,
)),
ReplicationReceiver::default(),
ReplicationSender::default(),
lightyear::prelude::Client::default(),
client_conn,
))
.id();
commands.trigger_targets(Connect, client);
}
The server_conn should be added on a ClientOf entity, not on the server entity itself
The field interpolation delay has a weird name, how about we change for something like DiffPredictedInterpolated
Delay gives the impression that is a small buffer between interpolated and predicted so it doesnt well interpolated to agressively
I also would like to say I am about finished with my code just writing a lot of test for multiple differents scenarios (20 bullets at once), one sole bullet, bullets with dynamic bodies
Multiple collider
Oooh, that makes sense now. It's the "server-side" part of a connection!!
Thank you, that's the piece I was missing
Trying it out, I think I am still plugging it together wrong 🙈
My first thought was that ClientOf is just part of the puzzle, as it doesn't have any way to relate back to the Server. So I was thinking that was the job of LinkOf!
And it does work now, but it's currently spamming on top:
https://github.com/cBournhonesque/lightyear/blob/14365d2b9c7fe134c8d8fc035d56447bd83fbb40/lightyear_udp/src/server.rs#L112
The comment above makes me think this is benign? I can filter it out for now if I can ignore it.
A networking library to make multiplayer games for the Bevy game engine - cBournhonesque/lightyear
Yes you can probably ignore it
- The inputs are previously unsent inputs, not no inputs
Yes, I did add the lightyear plugins before adding FrameInterpolationPlugin
If by lightyear plugins you mean lightyear::prelude::ClientPlugins
Here are all my features:
lightyear = { version = "0.23.0", features = [
"webtransport",
"netcode",
"interpolation",
"prediction",
"replication",
"input_bei",
"avian2d",
] }
lightyear_frame_interpolation = { version = "0.23.0" }
avian2d = { version = "0.3.1", features = [
"2d",
"f32",
"parry-f32",
"parallel",
"serialize",
"enhanced-determinism",
] }
does anything there seem odd?
Here is how I am adding frame interpolation:
fn add_visual_player_interpolation(
trigger: Trigger<OnAdd, Position>,
filter: Query<(), With<Predicted>>,
mut commands: Commands,
) {
let entity = trigger.target();
if filter.get(entity).is_ok() {
info!("Added visual intepolation to {entity}");
commands
.entity(trigger.target())
.insert(FrameInterpolate::<Position> {
// We must trigger change detection on visual interpolation
// to make sure that child entities (sprites, meshes, text)
// are also interpolated
trigger_change_detection: true,
..default()
});
}
}
Do you have a MacOS? I am on Mac
I am on mac M2
Is local to remote entity mapping supported for crossbeam channels?
I have a "client" command that is sent via a crossbeam channel and i would like to include a vec of entities that the server should see as its remote entities and use for an action a client would like to do.
afaik add_map_entities only exists for MessageRegistration.
However, crossbeam_channel does not expose this when constructing a new channel.
So now I am wondering what the recommended way to support automatic local to remote entity mapping is when using crossbeam channel messages or if the "solution" is: do not use them for this case and regular message channels instead.
Thanks for any response in advance🙂
Yes entity mapping is independent from the IO used. It's also independent from the channel, it's purely a property of the Message
@pine cape could you map the Client entity and the ClientOf entity? i want to have some "client information" that stays on the Client/ClientOf entity
Adding ClientServerStepper , will automatically simulate a scenario where I have n clients connected to a server in a dissociated app?
https://github.com/cBournhonesque/lightyear/pull/1201 - @pine cape Pr is a little unfinished but I have a few doubt on how to continue with testing, would appreciate some input
This PR brings the following improvements to the current API.
Now we can have multiple lag compensated colliders
We do not need to care about the system ordering of avian because we do a slight &a...
If anyone finds out the issue with 1/2/3 please remember to let me know
hey does anybody can explain what i'm doing wrong?
when trying to run or build the example i get a cmake error, but i've installed the c++ build tools and can run other bevy projects without any problems. Does anybody have an idea?
you need the cmake application
Yes they are already mapped
thank you that helped, didn't know that there is a separate application outside of the c++ build tools
FYI the cmake application is installed with the build tools if you follow the instructions on bevy, but it doesnt seem to be added to your path env variable. another fix is adding it to your path
pretty sure that was the main Problem, unfortunately didn't tested that before but i got simple_box working now, i'm happy and want to dive in deeper later.
The books and issues mention being able to replicate Resources 🤔 Is this (still) a feature? I can't seem to find anything pertaining to it in the current version/main branch.
Not a biggie if not, but if its around but renamed I'd just use that 😄
It's not possible anymore, I'm waiting for Resources as Entities that will be enabled in bevy 0.18
Well, time to not use Resources then alright!
I feel I'm also running a bit against a misdesign of my app.
What I am trying to do is keep a 'Lobby' entity where each Player gets an Entity that contains public info, like name, ping, if they are host etc... that gets replicated to each client.
My 'problem' right now is what would be the best way to find a specific child entity of that again? There is the ControlledBy and its Target, but they don't expose the list (unlike Children). So I can't easily iterate the controlled entities of a given connected Peer.
I am currently iterating the Lobby entity for its children, but that is n^2 if they all update and that irks me lol
So I have
Lobby -Children-> [Player1, Player2]
Server -Server-> [Peer1, Peer2]
Peer1 -ControlledByRemote-> [Player1]
And when I receive a Message through Peer1 I currently have to iterate Lobby for its Children and match the PeerId to find the correct entity, which seems not how it should go?
You could add the components directly on the Peer entities
Or you can store the mapping in a resource
Hi! I am using lightyear from the main branch of the git repo. For context, I am trying to make a first person character controller for a co-op game. For simplicity and quick prototyping, I wanted to transfer authority of the spawned character controller to the connecting client. The book and the distributed_authority example mention the method "transfer_authority()", but it doesn't seem to exist anymore?
I tried to run the distributed_authority example and got a bunch of errors:
// Many more errors above, but this one is about the missing method.
error[E0599]: no method named `transfer_authority` found for struct `EntityCommands` in the current scope
--> examples/distributed_authority/src/server.rs:118:22
|
116 | / commands
117 | | .entity(ball_entity)
118 | | .transfer_authority(PeerId::Client(player_id.0));
| | -^^^^^^^^^^^^^^^^^^ method not found in `EntityCommands<'_>`
| |_____________________|
|
Is there anything I'm missing, a new way of achieving the same behavior or even a workaround that I could use in the meantime?
What led me to try making the character controller client-side is an issue with rollback and "bouncing" on my character controller, and I couldn’t find a way to fix it. I ran the avian_3d_character example, and it works as expected with cargo run -- host-client -c 0. However, when running a server and a client separately (cargo run -- server & cargo run -- client -c 1), I see the same effect as in my project: (in the video, I move left twice, then right twice, and each time the capsule rolls back when I stop pressing the keys. Tried with the lightyear 0.23.0 from crates.io and the problem was already there.).
@hot cloud can't help you any further but just an fyi that the book is quite out of date in most areas
I suspected as much, thank you
What would cause only some entities to be replicated?
I have an Entity with Replicate and NetworkTarget::all. The idea being that it gets transitively given to all its Children.
When something gets the LinkOf, it gets a ReplicationSender and once its connected they do receive some of the entities. But not all? Most notably, on my client host I see all three entities, but then on all other connected clients I only see the ClientHost entity. Which does get replicated. If I change something on it, it gets updated for the others
The server has 3 and the other clients all only get 1 🤔
I do get a lot spam like this in the console I just noticed setting to debug, but I can't tell what this means?
Adding a Replicate to all entities seems to do it, but this is very counterintuitive? Why would only some entities get replicated in the first case? Especially it looked like the whole hierarchy would get replicated.
Aka, if I had replicate to the parent, its child and a grandchild got replicated.
And if I add it to all children recursively it all works.
I'd expect in the first case then only the entity itself gets replicated?
I feel this is because of this line: https://github.com/cBournhonesque/lightyear/blob/14365d2b9c7fe134c8d8fc035d56447bd83fbb40/lightyear_replication/src/hierarchy.rs#L120
As it only handles direct descendents of Replicate but children only have ReplicateLike ?
I'll try to see if changing that query could fix it 🤔
Yeah that fixes it! Will write some tests and send a PR
Yes the authority example and code is out of date. I haven't ported it yet after the refactor since it seemed like not many people were using it.
You can look at the code in lightyear_replication/authority. I might also have some unit tests
I haven't tested it, but maybe this issue mentions a fix? https://github.com/cBournhonesque/lightyear/issues/1113
I don't have my laptop, but you're saying that I currently only handle children of entities with Replicate, but not grandchildren?
Thank you, I'll take a look.
Yes, since the query only looks at children of Replicate entities
So it couldn’t see ReplicateLike
But that's just for the root, for children we use children_query
Yeah but if you insert deeper into the hierarchy the root never gets notified
So
E -> E1 -> E2
If you add a Child to E1 or E2, E will never have its Children changed, and so the root_query will not return it and thus children_query will not be used
how do you handle networking fps player/camera rotation inputs when cursor grab is active? can you get the BEI mouse motion value before a mock is evaluated that same frame? I basically just want to send the server the player rotation (yaw, pitch) after the mouse motion instead of the mouse delta to prevent any need for correction.
Edit: got this working through separate context schedules
As your are in vacation would you like a meme to spice things up
@pine cape Any ideas on how I can map the entities inside a trigger message? Like this one , note when I add_map_entities to it, it only seens to affects the target entities the ones i point to in trigger_targets and not to the ones inside the message
#[derive(Event, Clone, Copy, Deserialize, Serialize, MapEntities)]
pub struct ChallengeSent {
/// The entity who sent the challenge
pub from: Entity,
/// The entity who receives it
pub to: Entity,
/// The amount betted
pub bet_amount: BetAmount,
}```
I can just check RemoteEntityMap and go for there, but I was wonmdering if I am missing something in the api
Just deriving MapEntities and then calling add_map_entities in the protocol should work
When deriving MapEntities, you need to add the #[entities] to the fields that you want mapped. You should check the docs for MapEntities
I'm wondering if it would not be easier to just merge the Interpolated, Predicted, Confirmed entities into one.
My main motivation would be to remove the need to have complicated entity mapping logic when a client sends a message to the server that mentions an interpolated or predicted entity.
Some possible questions:
- how to handle despawns? What if the entity is predicted to be despawned?
-> we already handle this by not actually despawning the entity but instead we temporarily disable it. - how do we distinguish between the confirmed and the predicted value of the component?
-> I would say that in the immense majority of cases the user is interested in theInterpolatedorPredictedvalue of the component and not the confirmed one. I think it's also extremely rare to need to have an entity be both Predicted and Interpolated at the same time. Therefore we could change the 'confirmed' value to beConfirmed<C>and the predicted/interpolated component to be simplyC
Of course it would also be a massive breaking change, but it might make things easier in the long run
The pointer components are very handy (predicted/interpolate), perhaps you should just make it so they are relationships?
For me it makes a lot sense to have distinct entities with their own components. For example: If i want interpolated transform i just query interpolated, how would i do that with this API I think it would be a little more dirty.
I also dont like the idea of centralization, but that is just my opinion.
The mapping used to be a pain in the ass but nowadays I wrapped my mind around it
We would still keep the Interpolated, Predicted, Replicated marker components
so you can still query Query<&Position, With<Predicted>>
querying the confirmed component becomes harder, because you would do Query<&Confirmed<Position>>; but querying confirmed components seems to be pretty rare. I don't need it in any of the examples
Ah so the predicted entity/interpolated becomes the central point?
Yes, basically. The confirmed components get added as special Confirmed<C> and the predicted or interpolated component value is just C (+ we add a Predicted or Interpolated marker)
not gonna lie
I dont hate that idea it would certainly simplify my customizer API
As currently it goes something like: predicted entity needs new visual -> send message to server -> mutate component -> from confirmed to predicted listen to mutations, do not create visuals for confirmed
When you send message to server, you need to convert from predicted to confirmed, no?
Also we wouldn't need PredictionMode::Once or PredictionMode::Simple; maybe for those modes the component can be replicated directly as C instead of Confirmed<C>. Only for PredictionMode::Full or InterpolationMode::Full do we care about distinguishing between Confirmed<C> and C
well we first update predicted or else it feels delayed. but yes
that would also simplify my protocol a lot, as now they feel like magical required component that pop from nowhere
I can try to implement this in a branch and see what it looks like
I published a new version 0.24.2 that contains recent bug fixes; it will be the last version before bevy 0.17
@pine cape crates.io still shows only 0.24.0
ah, for the BEI one only
no, wait, all subcrates are 0.24.2, but the metacrate (lightyear proper) and the workspace are on 0.24.0?
thread 'main' panicked at /home/hukasu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/raw/mod.rs:797:9:
assertion failed: index < self.buckets()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `lightyear_replication::receive::ReplicationReceivePlugin::apply_world`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
corrupted size vs. prev_size in fastbins
on 0.24
Oops
this happens when i have Replicate on the ClientOf
Yes replicating the ClientOf or Client itself is not allowed rn
Which shouldn't be the case, since Client and ClientOf are mapped
Maybe you're adding Replicate too early, before the mapping is done?
So you're replicating ClientOf, which creates a new entity on the client (in addition to the Client entity)
In 0.24, the Client and ClientOf entity are already mapped. Can you try doing this after 1 second? To make sure the mapping is done
i was having an issue were 1 of the components would intermitently not replicate, after adding the Replicate earlier i haven't seen it happen
@pine cape is there anything that i can query to wait for the entity mapping?
Right now, no, but we should add something
@pine cape Interesting development:
Upgrading to the latest lightyear version almost** solves the input lockup. Here's what I'm noticing now:
Input no longer locks up for a long time, but now I am seeing 1-2 seconds at most of delayed input handling. Basically for half a second I see my player move based on very recent past inputs.
Heres a video
You can see there are some lockup stutters, but they are short-lived now.
It seems like the input is being handled later than when the input was sent, do you know why that would be the case?
im attaching a BEI input map to the Predicted player
one way to do this is only adding Linked or Connected after the mapping is done
I'm not sure what i should be looking for in the video
i'm having a problem now that i am replicating a relationship, but it arrives on the client before the entity that maps to the relationship is received
2025-09-17T14:01:45.911927Z ERROR lightyear_serde::entity_map: Failed to map entity 587v2#8589935179
so i would say the same thing about the ClientOf, only have the Replicating component added after the mapping is established
i added an arbritary 5 second delay on creating the relationship and it still failed to map, so there is something else on my ordering
@pine cape i saw on the ComponentReplicationConfig that you can have a compontent be registered, but not automatically replicated, so can you have a component on an entity with Replicate::new(NetworkTarget::All) but that one component be Replicate::new(NetworkTarget::Single(...))?
You can override the replication target of a component with ComponentReplicationOverrides
@pine cape Is there a good trigger to check when server can start replicating an entity?
or can i just start replicating entities on startup?
Ah i found the started component
I believe you can start replicating at startup, the server will start replicating only after Connected is added
I'm working on adding some opiniated network UI to help debug
it's loosely based on the one from avian right now, but I want to improve it
I opened this thread to gather ideas: https://github.com/cBournhonesque/lightyear/discussions/1213
That means that big massive code of that weird performance egui will be deleted (I personally think that would be ideal as I hate how much that guy increases compile time) although he is diablable
What performance egui? Bevy_metrics_dashboard?
iyes_perf_ui is kinda extensable, no?
I'm using metrics and not Diagnostics, so I think it's easier to roll out my own
@pine cape are you sure that there is a mapping on the client between the ClientOf (on the server) and the Client (on the client)
this log is on the client
2025-09-18T15:47:09.603285Z ERROR lightyear_serde::entity_map: Failed to map entity 598v2#8589935190
2025-09-18T15:47:09.603469Z WARN bevy_ecs::relationship: The psycho_core::player::PlayerOfClient(PLACEHOLDER) relationship on entity 450v2#8589935042 relates to an entity that does not exist. The invalid psycho_core::player::PlayerOfClient relationship has been removed.
598v2 is the id of the ClientOf on the server, and it does not exist on the client
Yes; you can add logs in the system receive_sender_metadata to confirm
ah, yes, updating to 0.24 fixed
@pine cape after upgrading to 0.24 i started getting this crash on Netcode(0) only
thread 'Compute Task Pool (8)' panicked at /home/hukasu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/raw/mod.rs:85:9:
Went past end of probe sequence
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `ReplicationSendPlugin::replicate`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
free(): double free detected in tcache 2
thread 'main' panicked at /home/hukasu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/raw/mod.rs:85:9:
Went past end of probe sequence
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `lightyear_replication::receive::ReplicationReceivePlugin::apply_world`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
double free or corruption (!prev)
Can you do graphs with that diagnostics UI?
if so I might ditch egui_plot for aeronet/visualizer
Probably related to https://github.com/cBournhonesque/lightyear/issues/1159#issue-3323405849
My UI is really barebones. But bevy_metrics_dashboard can print graphs and is pretty good
Might switch to it in the future then. I'm kinda annoyed with bevy_egui
It's still egui under the hood though
@pine cape has anyone complained about a stack overflow on the server?
thread '<unknown>' has overflowed its stack
fatal runtime error: stack overflow, aborting
Yes there is an issue open. Related to inputs?
the only difference between the previous commit and the current is bumping the version of lightyear to 0.24 and bei to 0.18
the server was open with just one client connected, and i was afk
But where did it overflow, what is the stack trace
#1189344685546811564 message that is all that gets logged
i saw an issue about subtract with overflow, but not stack overflow
even with RUST_BACKTRACE=full the only log is those 2 lines
Ah, I've never seen that stack overflow
How do you properly Replicate Components with Entities inside again? I couldn't find it inside the docs or examples. A hint do an example or docs would be sufficient 🙂. I can't find any myself. Do you map the entities manually via some external id? I thought I read something about some built-in map in lightyear somewhere before
Take a look at https://github.com/cBournhonesque/lightyear/blob/main/examples%2Freplication_groups%2Fsrc%2Fprotocol.rs#L263
You just need that + your component must implement MapEntities
Thanks, boss 🙂 works like a charm
Hey @pine cape I updated to the latest version of lightyear and my enemies are now 1) invisible when i spawn into the map 2) Show up when I walk into them 3) seem to have their positions between the server and client desynced because they are firing projectiles not from where they are at. Any ideas what changes would have caused this? (Enemies are interpolated)
That's from 0.23 to 0.24?
- seems expected; you probably make the enemy spawn a projectile as soon as the message is received, but instead the projectile should be delay-spawned to be on the interpolated timeline
- and 2) seem to be related to the visibility systems?
Ok:) thanks for the help!
Merged a first version of the debug UI
Each section is collapsible + you can filter by send or receive
nice that will be really helpful
oh pretty
Hello! I wanted to add an "Attack Hitbox" to my players to test the collisions of close combat weapons with enemies. Using the "avian_3d_ character" example, I added a child entity to the Character entity:
// Edit of the `avian_3d_character` example.
commands
.spawn((
Name::new("Character"),
ActionState::<CharacterAction>::default(),
Position(Vec3::new(0.0, 1.0, 0.0)),
Transform::default(),
Replicate::to_clients(NetworkTarget::All),
PredictionTarget::to_clients(NetworkTarget::All),
ControlledBy {
owner: trigger.target(),
lifetime: Default::default(),
},
CharacterPhysicsBundle::default(),
ColorComponent(color.into()),
CharacterMarker,
))
// Attack Hitbox collider
.with_children(|commands| {
commands.spawn((
Name::new("Character Attack Hitbox"),
Transform::from_xyz(0.0, 1.0, 1.0), // offset in front of entity
ColorComponent(RED.into()),
AttackHitbox,
));
});
// Shared observer system to add Sensor and Collider:
pub fn on_add_attack_hitbox(trigger: Trigger<OnAdd, AttackHitbox>, mut commands: Commands) {
commands
.entity(trigger.target())
.insert((Sensor, Collider::cuboid(0.5, 1.0, 1.0)));
}
However, the Hitbox Collider entity doesn't follow when the Character entity moves. I tried the same setup with Avian's character controller example (From Avian's github), and the Hitbox Collider followed its parent as I wanted.
Is this a difference in behavior due to how Lightyear handles things around Avian internally? Am I missing something obvious?
Yes there seems to be a problem with child collider sync: https://github.com/cBournhonesque/lightyear/issues/1128
By default I override the SyncConfig of avian here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_avian/src/plugin.rs#L109-L109 which maybe messes something up? I haven't had the time to experiment more
I would try with self.update_syncs_manually=True to see if that works
I made some progress on the projectiles example; I got linear-bullets shot by the current player working.
Next steps:
- make sure that shots fired by remote players are replicated properly
- bullets are currently Replicated individual entities. That's very expensive in terms of bandwidth, instead we want to just replicate the initial direction
Thank you, I'll try
I found this bug when I changed my entity hierarchy from using Prediction to using Interpolation.
Description:
ChildOf sync will fail randomly when adding interpolated entities to interpolated hierarchies. Some entities in both the Confirmed and Interpolated hierarchy will not have a ChildOf component while others will.
How to reproduce:
- I did not register ChildOf in my protocol because it is already registered here:
https://github.com/cBournhonesque/lightyear/blob/e86c042f497707a730480fac66298e20fd9f1041/lightyear_interpolation/src/plugin.rs#L173 - In the StartUp schedule I made a "4 deep" hierarchy of entities that are interpolated to the client. (the client receives these correctly 100% of the time)
- In a system that runs right after in the Update schedule, 16 new interpolated entities are added to the hierarchy and replicated to the client.
- In the most recent test only 5 of those entities had a ChildOf component while the other 11 entities from both the Confirmed and Interpolated hierarchies are left orphaned for a total of 22 orphaned entities. This number seems to vary with the exact same build sometimes only 2 new entities had their ChildOf component.
Relevant logs:
Just a guess, but maybe a child entity is replicated to the client before it's parent entity, making it's ChildOf component invalid and thus removed?
When I was making this with a Predicted hierarchy I would get the "Failed to map entity" errors but the ChildOf components were never removed.
You're using InterpolationMode::Full?
Components are not synced from the Confirmed entity to the Interpolated entity right away, only when they have received 2 confirmed updates
ah my bad; it's mode Simple
Are you replicating all these entities in the same ReplicationGroup?
If not there is no guarantee that they will be replicated together, so the entity_map will fail for some of them
Adding ReplicationGroup fixed it, totally forgot to add those to the player entities. Thanks!
aha yea i also always forget
@pine cape Mr Peri, how expensive would be to replicate 32 entities. That are not predicted or interpolated?
But all would have the component inserted into them immediatelly
guess i can find out here
Cheap
I know I am but you dont need to tell on my face 😩
When I spawn a predicted entity as a child of an interpolated entity, it gets placed in the confirmed hierarchy instead of the interpolated hierarchy. Is there a way to put predicted entities inside the interpolated hierarchy?
why would you make a predicted entity, a child to interpolated? Cant you just make a relationship
I making a solar system simulation, the planets are interpolated, and the child moons that orbit it are also interpolated. I want to spawn a predicted character as a child of the moon entity. The confirmed character is spawned correctly, but the predicted character is also in the confirmed hierarchy. I was wondering if there was a way to do this or if I should just implement my own system to set the parent.
I think it would be easier for you to just make a different relationship in this scenario and distinguish replicate types. Instead of using inheritance as the interpolated timeline will conflict with the predicted one and cause rollbacks associated with the inheritance of transform/pos/rot of the predicted character
Unless you want character to not inherit the moon transform
I have my own custom positioning system that does not rely on transform. Having a predicted entity as a child of an interpolated would have no rollbacks in my case, I simply need the hierarchy for querying sibling entities. What do you mean by using a different relationship?
Hmm, i guess then all you need to do is move the ChilfOf component to the predicted entity
I already made a system that just swaps it to the interpolated hierarchy from the confirmed. I noticed this issue: https://github.com/cBournhonesque/lightyear/issues/1208 which would probably solve all of this so I'll just wait for it to be implemented.
There's no automatic way, I'm guessing the ChildOf component gets synced to the Predicted entity using the PredictedManager's mapping (which doesn't get contain the entity since your parent is interpolated). You need to manually map ChikdOf to the Interpolated entity
the 1-entity solution is appealing but also removes some flexibility:
- for example if the Confirmed entity gets despawned at tick T on the server, we might want to despawn the Interpolated entity only when the interpolation timeline reaches tick T on the client. This is easy to do with the current design but it's much harder with the 1-entity approach
I've tried implementing the 1-entity solution. It removes so much code and complexity that i think i'm going to go ahead with it.
But muh interpolation despawn
we'll find another way 🙂
hi! I'm trying to debug why my server deployed to GCP suddenly stops responding to connect token requests. It lives for about 12h (sometimes less, sometimes more), and then incoming requests just time out during the TLS handshake:
curl -v https://gameserver.minecrawler.pp.ua:5001
* Host gameserver.minecrawler.pp.ua:5001 was resolved.
* IPv6: (none)
* IPv4: 35.208.181.89
* Trying 35.208.181.89:5001...
* Connected to gameserver.minecrawler.pp.ua (35.208.181.89) port 5001
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/cert.pem
* CApath: none
* SSL connection timeout
* Closing connection
curl: (28) SSL connection timeout
On the server end, I can't see any error/warning logs, the process is still alive (though I just thought I need a another confirmation of that), but the timeout error suggests that the socket is still open at least (otherwise it would be a connection refused error).
This is not a lightyear specific issue, so apologies if is this off-topic, but I figured that some people in this channel go through this kind of setup and might be able to notice a problem in my code.
this is my gist of the code that serves http requests via hyper (I'm also using tokio_rustls here and executing tasks via Bevy's IoTaskPool through the async_compat layer), assume that the tls feature is enabled:
https://gist.github.com/vladbat00/6cac156f98c9dabe3706b7dceb9b2ca6
Any help or ideas would be much appreciated!
Hello all 🙂
I hope it's the right place to ask questions, I'm trying to understand multiplayer in general and I'm trying to do a simple capsule FPS template (really basic).
https://github.com/ierezell/yolo
It works fine, player spawn, fall down a bit but when it collides with the ground, the capsule starts slight infinite boucing.
I've tried to add_should_rollback, damping etc... but nothing do.. I guess it's a problem of sync.
If anyone have any indsights, thanks a lot ! 🙂
Try adding PredictionMode::Once to these:
Also make sure you're only adding visuals to predicted entities here: https://github.com/ierezell/yolo/blob/main/crates/shared/src/render.rs
Contribute to ierezell/yolo development by creating an account on GitHub.
For walls, floor etc.. yes it's only visual (meshes) on client side.
The functions are shared, so server will add it in non-headless/"visualization mode".
But from what I've read (and oh boy it's more complex than I thought), I could have client interpolating and server being authoritative for all the physics but it brings more input lag.
So what I'm trying here (if i'm correct...) is to also have the client run physics (so I'm adding my PlayerPhysicBundle in the client).
I've tried adding PredictionMode::Once and double checked the visuals but still, the capsule is bouncing (and so is the camera).
Server side, the position is stable everytick, but on the client Y is bouncing.
Thanks a lot for the fast answer and the feedback !
Edit: I'm pushing the comments as fast as I can so the repo is up to date
Hmm, it consistently hangs after responding to 25 requests on my windows PC.. wut
If you're predicting physics on the client, you also need to add physics components on the walls and floor on the client, otherwise the result won't be the same as on the server
I found it today.... And I felt so stupid... 😅 But exact, it was no bouncing, it was client falling through and server correctly rolling it back
Thanks a lot for the help! I'm a bit less noon now
Did you figure it out?
I'm trying to enable replication for a bunch of level entities that are spawned separately on client/server from a gltf scene at different times. I tried adding prespawned with a custom hash after it's loaded and have had partial success depending on ordering/timing, but it seems like it's not meant to be used the way I want. I'm basically trying to load the level on the server when it starts, but then let each client load it on their end when they connect and then map some of the entities for future changes. Can I just manually add the mappings on the client and have it replicate from then on or would it cause issues?
Yes you can add the mappings on the client or on the server directly and it would work; but i'm also I'm modifying Prespawning so that it would work like you intended.
(i.e. you can add Prespawned on both the client and server entity, and then when you try to replicate it will just re-use the existing Prespawned entity instead of spawning a new one)
I'm getting an error I think because this line was removed in 0.24. nvm. it's working on mainEncountered an error in system <bevy_app::app::App as lightyear_steam::SteamAppExt>::add_steam_resources::{{closure}}: Parameter NonSend<SteamworksClient> failed validation: Non-send resource does not exist
that's good to hear! it's not a big deal to do it myself, but just wanted to make sure I wasn't fundamentally missing something about the mapping/replication behavior
What's the best way to debug why a component is not replicated when it's set to PredictionMode::Once and InterpolationMode::Once? I don't ever see any logs on the server from lightyear_replication::send::sender when I create an entity with the replicated component
Didn't have the time yet. I think the first thing I'll try is to get rid of the compat layer and spawn stuff directly on tokio executor
Oh, and I also wanted to trace-log what's happening on the hyper end. Because, effectively, it never writes the last response, and then the whole thing deadlocks somehow
Well, hyper trace logs didn't shed light onto anything, but replacing Bevy's IoTaskPool with spawning just via tokio has fixed it.
My guess is that the auth example in the repo is also affected by the issue (haven't checked it though)
And the entity is replicated?
How does replicate_once work for immutable components? Will replaced components (i.e. inserted more than once) will also get replicated?
Yes, it has Replicate::to_clients(NetworkTarget::All)
I think replaced components won't be replicated if using replicate_once, since it's using the Added query filter which AFAIK checks if the component was newly added
@pine cape You know something that would be great for the api
Making it so rooms have native entity map, as I think that would avoid the whole lobby shenanigans.
many-to-many relationships
Can you elaborate?
but what was your pain point?
I've been running into a weird issue in a setup with a fully server authoritative setup without any client side prediction etc. (only replicating to clients) everything works fine. Then I switch over to a HostClient setup (working fine) but then switching to a HostClient setup with a custom scheduler (using godot-bevy, which manually triggers FixedUpdate and Update based on external (godot) schedules) and then all of a sudden after a few seconds, it would seem like the replication on the <Replicated> entities break.
I've done extensive logging and it seems like this might be related:
15:11:25.443 D Setting the latest_tick Tick(172) to tick Tick(28388) because there hasn't been any new updates in a while @ src/receive.rs:
This happens right where the sync happens (the Replicated component gets stuck at the value) other events/messages still function (like despawning).
Now my question: I suspect that this is related to the Scheduling of replication and the TImeline, but in the host client mode both the server and client are using the same FixedUpdate (/Update) schedules, so I don't get why it would matter who often or when they are called (- as godot is controlling them).
My suspicion is that lightyear uses some other schedule internally for some replication tasks or timeline progression, which is not working correctly.
I understand that this might a bit vague but if someone has some insight it would be greatly appreciated as I spent a lot of time debugging this issue.
Does custom-scheduler without HostClient work?
Yes, when I run the client through godot-bevy (which simply manually calls Update and FixedUpdate) and the server is using Bevy default scheduler, then it works. Even though this is the opposite of what I expected.
Ok, and could you clarify exactly what is broken?
If I understand correctly you're using: Host-Client App that has a Server and a Client and uses godot-bevy + remote Client using godot-bevy.
And an entity is being modified on the Host-Client App but the updates are not replicated to the remote client?
No, it's simply a single client that also acts as a server. No other client. I'll make it little clearer:
I have:
- shared // this contains all server side systems and these act on FixedUpdate (or sometimes Update)
// the systems here only operate on entities Without<Replicated> to avoid running on Replicated entities in a HostClient scenario
- server // this wrapper that simply spawns a server entity and starts it, while using shared to authoratively control entities.
- client // the client contains 1. a godot-bevy version and 2. pure bevy version. The 2. version simply doesn't render anything.
Now when running in a server + client setup: both versions work. Server + Godot-Bevy Client (1.) and Server + Bevy Client (2.)
When running as HostClient, :
- the server systems simply run inside the same bevy app as the client.
- BUT: the pure bevy HostClient version works, while the godot-bevy HostClient version stops replicating some components after a seemingly random time.
I attached videos.
The server + client setup with both clients: -- This works fine
(bottom is server, top is client)
https://streamable.com/3286k7
Now HostClient:
The first start using cargo r is the pure bevy version
The second start using cd .. && godot is the godot-bevy version which breaks.
https://streamable.com/o3cab3
What to look for:
(here it breaks:)
// desync example in HostClient with godot-bevy version.
GODOT transform : [336.74863, 212.15329, 0] // this is always coming from the client
new position in pathing: [471.8, 207.9] // this comes from the server
The client logs by quering for With<Replicated> entities and then logging the position.
Sorry if this is still messy.
No, it's simply a single client that also acts as a server. No other client
A client that also runs server logic locally. There is no remote client in the broken example but simply the shared world of server and client.
I suspect that Setting the latest_tick Tick(172) to tick Tick(28388) because there hasn't been any new updates in a while @ src/receive.rs: might be a symptom as it seems to happen exactly when replication stops happening.
So in your host-client setup, you only have a single App? You're not testing that updates are being replicated to another app?
Because in a host-client setup, there is no replication from the Server to the HostClient since they are both in the same app. I just add a Replicated filter for convenience, but it should be the same entity
Really? You are saying it should be the same entity in HostClient setup and lightyear Replication doesn't spawn a new entitiy that's being replicated by the entitiy with Replicate?
So should there be an entity with With<Replicated> and With<Replicate> at the same time, then?
Yes, in a HostClient setup, if you add Replicate to an entity that is meant to be replicated to the HostClient, I just add Replicated on it since the server and the client are in the same App. There is no replication messages to be sent.
So should there be an entity with With<Replicated> and With<Replicate> at the same time, then?
Yes exactly
That's why i'm a bit confused that your Godot Transform doesn't match exactly the position in your host-client log
Wow, that's useful. But that makes my issue even more confusing to me.
I see this log:
GODOT transform : [336.74863, 212.15329, 0] // this is always coming from the client
new position in pathing: [471.8, 207.9] // this comes from the server
Both are from the same entity 🙂 How can that be haha, I'm flabbergasted
It is possible that I actually forgot to disable replication messages being sent from the HostClient Sender to the HostClient Receiver (which are the same entity, because a HostClient is a entity that has both Client and ClientOf)
I should maybe add a Without<HostClient> filter here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_replication/src/receive.rs#L91
How do you detect HostClient? On my end I didn't configure anything special expect for having the ServerConfig and ClientConfig in the same app.
So I didn't forget to add anything, did I?
Should I clone the repo and add that filter locally and test again?
But weirdly, my issue only appears after a while. It doesn't immediately break. (at least it seems that way)
https://github.com/cBournhonesque/lightyear/blob/main/lightyear_connection/src/host.rs#L69
HostClient is added to any Client entity which is also a LinkOf(server)
Should I clone the repo and add that filter locally and test again?
Sure, I would first enable lightyear_replication::receive=trace logs and check if you're receiving replication UpdatesMessage
like 15:11:25.416 T Received message "lightyear_replication::message::UpdatesMessage" on channel ChannelKind(TypeId(0xc099382d27579fd2a33001d7d7139452)) @ src/receive.rs:147
?
yes
Yes, I do receive that.
Alright, so I simply add Without<HostClient> here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_replication/src/receive.rs#L91 ?
Will try
yes, and maybe here too for good measure: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_replication/src/send/buffer.rs#L73
Is GodotTransform an equivalent of the bevy Transform? Is there a system that converts from your position to a GodotTransform? In which system set does it run
Still trying to understand how they could be out-of-sync when it's the same entity
Yes, but I'm simply logging the bevy transform and not the godot one.
I'm genuinely impressed by you @pine cape . That seems to solve the issue. HostClient and Client + Server both seem to work after testing the previously failing behavior successfully a couple of times.
It only cost me 2 days 🙂 , but as long as that solves it, I'm incredibly happy. Will you add those changes?
aha well i'm pretty familiar with the code base; glad I could help!
I also tihs is pretty important since before we were wasting CPU time to write/read all these replication messages for the host-client
I will add these changes for bevy 0.17, if that's ok
sure, as long as I don't have to rewrite all my codebase because of 1.6 million other major changes, I'm fine with that 🙂
Thanks again.
Hm there will be one other big change; which I think will simplify things in the long run: the Confirmed/Predicted entities and Confirmed/Interpolated entities are now merged together
I have a question about deterministic prediction.
In the projectiles example, a bullet is spawned on the server with DeterministicPredicted and without Replicate.
Q1. If a bullet is spawned and then a second client joins later, wouldn’t that client not receive that bullet? My game has long-lived projectiles/entities, so late-join visibility matters.
Q2. In my setup, adding Replicate on the server gave me errors, so I switched to “no client spawning; server spawns with both Replicate and DeterministicPredicted,” which seems to work (though I'm not sure if there's desync with this). Is this the correct approach for long-lived projectiles? If not, what’s the recommended pattern (including for late joiners)?
See tentative PR: https://github.com/cBournhonesque/lightyear/pull/1229
For which mode, OnlyInputsReplicated?
Q1. OnlyInputsReplicated clients only exchange inputs and run the simulation deterministically based on that.
When a client joins late you have 2 options:
- the new client receives a history of all inputs sent by all clients since the start of the game, and replays them. (in which case they would simulate the long-lived projectile that was shot before they joined). The issue is that it may take a while to simulate that
- the server sends a one-time snapshot of the world, and then continues with input-only replication
I haven't tried any of those, but I guess what I could do is that when the client joins, the server would replicate to them all existing long-lived projectiles. (For that, you would have to also run the simulation on the server, which is possible since they receive all inputs)
Q2.What kind of errors did you get? I don't really understand what you meant by “no client spawning; server spawns with both Replicate and DeterministicPredicted,” .
I open a PR, which you don't need to merge with the changes, just so you don't forget to add it yourself when the time comes 🙂
Yes I was referring to OnlyInputsReplicated, sorry for not clarifying.
The error I was referring to happend when doing something like:
- server: spawn(Replicate, DeterministicPredicted, missile_bundle)
- client: spawn(DeterministicPredicted, missile_bundle)
But the error was due to another part of my code.
For Q1, would doing something like:
app.register_component::<Position>()
.add_prediction(PredictionMode::Full)
.add_interpolation_fn(Position::lerp)
.with_replication_config(ComponentReplicationConfig {
replicate_once: true,
disable: false,
delta_compression: false,
});
add_non_networked_rollback_systems::<Position>(app); // not sure if this is needed
Work as the server sending a snapshot of the current world?
So instead of having fully non-networked components you'd just have replicate_once and then its inputs only from there. I just tried it and it seems to be working.
@pine cape got a strange one for you. I get a panic on the client when connecting if the server has somewhere between 467 and 471 total entities.
thread 'main' (612209) panicked at /home/brain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.15.5/src/raw/mod.rs:85:9:
Went past end of probe sequence
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `lightyear_replication::receive::ReplicationReceivePlugin::apply_world`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
corrupted size vs. prev_size in fastbins
Debugging the stack seems to point to this line as the potential culprit:
https://github.com/cBournhonesque/lightyear/blob/0.24.0/lightyear_replication/src/receive.rs#L779
In my case these were the values that function was called with:
local_entity: 344v1
group_id: 4294967764
Running version 0.24.2
These panics occur because we unsafely get the EntityMapper from the receiving Link entity. You probably have an observer that accesses the Link entity in some way which gives you undefined behaviour
Can you think of some observer you have on receiving a replicated component that could affect the Link entity or the EntityMapper?
Yes I think this would work, but ideally you would only replicate stuff the first time a client connects, not whenever a new entity is replicated
In this case you mean potentially an observer running on the client that modifies the link to the server?
I'm looking at observers that could have an effect on mapping too, but I'm not sure yet, could it be a mismatch in mapping from the server?
Oh interesting, I do see
2025-09-28T00:41:19.454174Z WARN lightyear_serde::entity_map: Failed to map entity 334v1#4294967630
earlier in the log
Looks like it might be because I'm replicating a player entity that has a ChildOf the client link so that it benefits from the despawn heirarchy when the client disconnects. I have disable replicate hierarchy on the player, but it must be picking up ChildOf anyway since it's on the entity directly.
I added ComponentReplicationOverrides::<ChildOf>::default().disable_all() so I can still have the player despawned by the client disconnecting and that seems to work.
Oh I may not need parent/child here and may be able to use a custom relationship instead since I'm using ControlledBy.lifetime
@pine cape Does the remote trigger also work for local triggers? Does it also run trigger:Trigger<E>, or just Trigger<RemoteTrigger<E>>
fn handle_external_goto(trigger: Trigger<RemoteTrigger<GoTo>>, mut commands: Commands) {
let message = trigger.event().trigger;
commands.trigger(message);
}
fn handle_goto(trigger: Trigger<GoTo>, query: Query<&PlayerOfClient>, mut commands: Commands) {
let message = trigger.event();
let player = message.player;
let room = message.room;
let PlayerOfClient(client) = query
.get(player)
.expect("Player entity to be the only one teleporting and to have player of client");
commands.trigger_targets(RoomEvent::AddSender(*client), room);
commands.trigger_targets(RoomEvent::AddEntity(player), room);
commands.entity(player).insert(CurrentLocation(room));
debug!("Changing rooms!")
}```
Would things like this be necesary @pine cape
Yes; it only triggers the RemoteTrigger for now
hey @pine cape , i've been messing around with the new debug ui (which is awesome, by the way). I noticed that in the avian3d example the rollback count seems to increase consistently (even when there are no inputs). is that expected or the sign of a possible bug? lmk if you'd like a video
It's expected, the reason is that avian uses some internal resources to speed up collision computation such as CollidingEntities and ContactGraph.
These are not replicated, so the physics computed on the client will be slightly different than the ones computed on the server. I think a way to lower the amount of rollback is simply to set a higher tolerance on the should_rollback functions
Actually in the case of deterministic replication (in the deterministic_replication example), only inputs are replicated and there are no rollbacks, because we also rollback the local ContactGraph and CollidingEntities resources
Maybe we just need to enable this in the avian3d example to also set the number of rollbacks to 0
I didn't try it though
seems like that would cause desync, right? maybe unless we used avian's enhanced-determinism
maybe i'm missing something - still seeing about 10 rollbacks per second with a very large margin on the should_rollback functions
🤔
@pine cape how many LocalTimeline ticks in a second?
LocalTimeline ticks once per FixedUpdate
fn buffer_rotation(
q_camera: Query<(&Transform, &CameraMode)>,
mut players: Populated<Entity, With<Action<RotateInput>>>,
mut commands: Commands,
) {
let (transform, camera_mode) = q_camera.single().expect("Camera to have a transform");
// Only do this if in tps mode
if camera_mode.ne(&CameraMode::Tps) {
return;
}
// Ordering matters here
let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
for player in players.iter_mut() {
// This creates a pseudo-action, that continouslys gets fired every state
let action_mock =
ActionMock::new(ActionState::Fired, Vec2::new(pitch, yaw), MockSpan::Manual);
commands.entity(player).insert(action_mock);
}
}
@pine cape Is this the only option when it comes to overriding actions? So we can input buffer unto client? It feels clunka, something more intuitive would it be to make it so action is capable of having quat I guess.
I think you can override with action_state.set_axis_pair
@pine cape any insights into what is causing these rollbacks?
sorry haven't had time to look, i'm focused on upgrading to 0.17
@pine cape did you happen to take a look at BEI input replication for HostClient, yet? No worries since you're busy with Bevy 0.17. I can wait until the dust settles.
Just wondering if I should check the current state again now, or wait until after the 0.17 upgrades.
probably wait until after 0.17
@pine cape Replicate like, on scenes are constantly warning that the global transform/visibility components are not available. I have no clue on why, as i do have transform synced
I guess something in your hierarchy is missing GlobalTransform. You should inspect the sender or receiver hierarchy
let parent_entity = commands
.spawn((
OverworldRoomMarker,
Replicate::to_clients(NetworkTarget::All),
Room::default(),
Name::new("Overworld room"),
identifier,
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
InheritedVisibility::default(),
// We dont need the scene garbase, we can recreate on client!
DisableReplicateHierarchy,
Phase(RoomSpawned),
))
.id();
let child_entity = commands
.spawn((
OverworldSceneMarker,
ChildOf(parent_entity),
Name::new("Overworld scene"),
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
InheritedVisibility::default(),
SceneRoot(gltf_handle),
Replicate::to_clients(NetworkTarget::All),
NetworkVisibility::default(),
// The deal here is that blender doesnt let you have the same name, we are going to abuse that
))
.id();```
The issue isnt that, is that when it arrives on client. It keeps saying this 2025-10-02T21:39:21.005827Z WARN bevy_ecs::hierarchy: warning[B0004]: Entity 434v2 with the GlobalTransform component has a parent without GlobalTransform.
Add GlobalTransform::default() to your parent entity
It's 'absurd' to use lightyear in a singleplayer game ? The fact is I really like/need Visual Interpolation* and Input
In which cases do you need InputBuffers for a single player game? I think you can just use BEI directly
You can use VisualInterpolation (renamed into FrameInterpolation) by using lightyear_frame_interpolation directly
same thing goes
It is when the replicated child entities arrive on client, they seen to only arrive with their given transform
Maybe becasue the scene root loads too fast?
GlobalTransform is probably only added on entities that are children of entities with GlobalTransform, you would have to add a custom observer
The parent entity has the Transform component when it arrives. At least it should have according to our protocol. And transform requires GlobalTransform. Also I tried that didnt work
@pine cape Mr Peri on another matter, I was trying to make it so my scene entities are mappable between server and client. I was doing something like this (where i basically transfer well the components amongs the sister scenes)
But I believe i can just make my client scene unto the aligned with lightyear scene, any ideas on how I can do that? Maybe moving lightyears components might work (although i am unsure)
fn transfer_meshes(
trigger: Trigger<SceneInstanceReady>,
probable_kin: Query<(Option<&ChildOf>, Option<&Children>)>,
children: Query<&Children>,
names: Query<&Name>,
mut commands: Commands,
) {
let temp_scene = trigger.target();
let repl_scene = probable_kin
.iter_siblings(temp_scene)
.next()
.expect("Scene to only have the replicated scene as a brother");
// Flatten the entities
let mut hashmap = HashMap::new();
for child in children.iter_descendants(temp_scene) {
let Ok(name) = names.get(child) else {
continue;
};
hashmap.insert(name, child);
}
for child in children.iter_descendants(repl_scene) {
let Ok(name) = names.get(child) else {
continue;
};
let Some(match_entity) = hashmap.get(name) else {
error!(
"Failed to find a matching entity via name on {} with {}",
temp_scene, name
);
continue;
};
// commands.entity(*match_entity).move_components::<(
// Mesh3d,
// GltfMaterialExtras,
// GltfMaterialName,
// MeshMaterial3d<StandardMaterial>,
// Aabb
// )>(child);
}
}
In the new version you can use the PreSpawned component to make sure that a server entity maps with a prespawned client entity
In 0.24 you say?
I was thinking I could just update the RemoteEntityMap
No in the unreleased version for bevy 0.17
I'm almost ready for the upgrade, mainly waiting for avian/aeronet to upgrade
hi! does RemoteEntityMap contain only Confirmed entities?
or can I get a remote entity by passing a Predicted/Interpolated entity as well?
It only contains Confirmed entities; this means that sending messages from client to server about a Predicted entity is pretty unconvenient, as you have to first map from Predicted to Confirmed.
This will be changed in the 0.17 release (which is almost ready) because the Confirmed and Predicted/interpolated entities are merged into one
https://github.com/cBournhonesque/lightyear/pull/1229 if you want to try it now
I am genuinely curious on how you are such a fast coder
what is your deal mr peri how in da hell you make 8 thousand lines of code in one to two days
A lot of it is automatic refactors
Hm i'm pretty happy about the merging of the Confirmed and Predicted entities, but it does some hard to fix problems
For example, when you first receive Position from the server, it is now inserted as Confirmed<Position>.
In the previous system:
- Confirmed entity receives Position. Spawns a Predicted entity with the same Position. The entity appears at the correct spot.
In the new system: - Entity has the Predicted marker and receives Confirmed<Position>. We add a RigidBody, which inserts Position::Default via required components. The entity appears at Position (0, 0) and is corrected to the Confirmed<Position>
I'm wondering if the solution might not be to insert BOTH Confirmed<C> and C on a Predicted entity.
Cis present so observers/required-components would work correctly.- There is no old value in the PredictedHistory so we start with an initial rollback anyway
just wanted to clarify that I wasn't missing anything
thx!
had to invent a bit silly SystemParam to map them without too much boilerplate in systems :D
#[cfg(feature = "client")]
#[derive(bevy::ecs::system::SystemParam)]
pub struct ServerEntityMapper<'w, 's> {
message_manager: Single<'w, &'static MessageManager>,
confirmed_query: Query<'w, 's, &'static Confirmed>,
predicted_query: Query<'w, 's, &'static Predicted>,
interpolated_query: Query<'w, 's, &'static Interpolated>,
}
#[cfg(feature = "client")]
impl<'w, 's> ServerEntityMapper<'w, 's> {
pub fn get_server_entity(&self, client_entity: Entity) -> Option<ServerEntity> {
let confirmed_entity = self
.interpolated_query
.get(client_entity)
.ok()
.map(|interpolated| interpolated.confirmed_entity)
.or_else(|| {
self.predicted_query
.get(client_entity)
.ok()
.and_then(|predicted| predicted.confirmed_entity)
})?;
self.message_manager
.entity_mapper
.get_remote(confirmed_entity)
.map(ServerEntity)
}
pub fn get_client_entity(&self, ServerEntity(server_entity): ServerEntity) -> Option<Entity> {
let confirmed_entity = self
.message_manager
.entity_mapper
.get_local(server_entity)?;
let confirmed = self.confirmed_query.get(confirmed_entity).ok()?;
confirmed.interpolated.or(confirmed.predicted)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServerEntity(Entity);
impl ServerEntity {
#[cfg(feature = "server")]
pub fn new(entity: Entity) -> Self {
Self(entity)
}
#[cfg(feature = "client")]
pub fn from_client_entity(
client_entity: Entity,
server_entity_mapper: &ServerEntityMapper,
) -> Option<Self> {
server_entity_mapper.get_server_entity(client_entity)
}
#[cfg(feature = "client")]
pub fn client_entity(&self, server_entity_mapper: &ServerEntityMapper) -> Option<Entity> {
server_entity_mapper.get_client_entity(*self)
}
}
and it works great
that's pretty good!
Had a look into that, it's because there is no tolerance for visual_velocity and angular_velocity.
Adding
fn linear_velocity_should_rollback(this: &LinearVelocity, that: &LinearVelocity) -> bool {
(this.0 - that.0).length() >= 0.01
}
fn angular_velocity_should_rollback(this: &AngularVelocity, that: &AngularVelocity) -> bool {
(this.0 - that.0).length() >= 0.01
}
fixes the rollbacks
Thanks for taking the time to look at this! Will take a look
I think BEI input replication for HostClient works. You can try it on the bevy_enhanced_inputs example
Is this on latest main, or 0.24.2? I just tried it in my codebase with the latter and I still get mapping errors for the host on a connected client.
Here's a log
I just tested on main
There are mapping errors but does the example work with host client?
Ah, I think we lost some context from our conversation a while back. I meant specifically input rebroadcasting, not just replication. My mistake that I didn't include that in my more recent messages.
The BEI example does not do rebroadcasting.
Host-client actions are not replicated to clients, however rebroadcasting messages do work, and then you get the errors I'm seeing.
I'll try updating the example for rebroadcasting and report back though.
Yeah, adding rebroadcasting to the BEI example on main results in the same errors I'm seeing in my codebase.
Thanks; fixed this for the next release in branch cb/confirmed-merge
Confirmed this fixed it, though I did need to increase the tolerance
Still having a weird issue with stuttering on the client controlled character, but i won't bug you about it until i have a better visual or more info
In HostClient mode?
No, I have been launching three windows for testing:
cargo run -- server
cargo run -- client -c 1
cargo run -- client -c 2
Though I just checked and it seems to be there in hostclient mode too
@pine cape for some reason it's a bit harder to see in videos, but if you look at the top right (client 1) which is where i'm sending inputs, you can see it's kind of jittery, compared to client 2 and the server where it's basically perfectly smooth movement
Maybe related to this? seems like the position is moved backwards on the client
Server doesn't have this and is smooth (as expected). let me know what you think!
Yes there's a problem in my branch, i'm not entirely sure what it is yet.
The deterministic-replication example is broken as well
The Last vs FixedLast is because of FrameInterpolation; Last interpolates between the last 2 FixedUpdate Positions.
Is this behavior different on server vs client?
or I suppose it would be Controlled vs not
FrameInterpolation is not enabled on server because of this bound: https://github.com/cBournhonesque/lightyear/blob/d4231144ea6d07793f88772f048ba69adfd65682/examples/avian_3d_character/src/renderer.rs#L74-L74
but yeah there's some tricky bug ..
you think the bug is causing that stuttering i'm seeing?
yea it's probable
The non-deterministic bug doesn't happen every time, which is 100% the sign of a missing system ordering constraint
those are quite hard to find unfortunately
frame interpolation causes stuttering, no idea on why
I believe the intention was to migrate to avian interpolation or something like that
But the moment I remove it everything was equal and worked fine, but no stutter
magic i guess
@unkempt sedge I noticed that as well. I'm wondering if avian and lightyear are overwriting the positions in a weird order
probably
the logic is full of rcs quite easy to be bugged so it is almost guaranteed I guess bevy should upscale a interpolation api sooner than later
Are you using FrameInterpolation<Transform> ?
No, the avian3d example uses Position and Rotation:
@pine cape Would there a problem to mark an entity as replicated before server actually being started?
just wondering if is something that was scoped
No there shouldn't be any issue
I think I fixed the deterministic replication bug.
For the avian_3d_character; does it look smooth to you with InputDelayTicks = 10?
It does look smooth to me. Which means that the problem is not related to FrameInterpolation, but to Prediction/Correction
I noticed something kinda weird when trying to figure out the frame interpolation/client input lockup.
2025-10-06T20:04:06.505081Z INFO bevy_vello::integrations::lottie::systems: animation controller transitioning to=down-stopped
thread 'Compute Task Pool (1)' panicked at /Users/simbleau/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lightyear_interpolation-0.24.2/src/interpolate.rs:213:21:
assertion `left != right` failed
left: Tick(5052)
right: Tick(5052)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'Compute Task Pool (3)' panicked at /Users/simbleau/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lightyear_interpolation-0.24.2/src/interpolate.rs:213:21:
assertion `left != right` failed
left: Tick(5052)
right: Tick(5052)
Encountered a panic in system `lightyear_interpolation::interpolate::insert_interpolated_component<avian2d::position::Position>`!
Encountered a panic in system `lightyear_interpolation::interpolate::insert_interpolated_component<avian2d::position::Rotation>`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
I am getting this pretty commonly when a new client joins