#lightyear

1 messages · Page 14 of 1

strong barn
#

This would be where it panics

pine cape
#

hm the new implementation is much simpler, do you want to try to upgrade?

#

i haven't release because aeronet hasn't been updated, but maybe I should ? UDP works

strong barn
#

I keep crossing my toes every upgrade hoping the input lockup suddenly goes away 🤣

#

I can try it out

#

I'm still on 0.16

#

Is main on 0.16?

pine cape
#

I guess I should just merge it

strong barn
#

ah. ok, I am not ready for 0.17

#

I'll circle back on it though

#

My project has a lot of dependencies that need to migrate to 0.17 first

pine cape
#

SG!

strong barn
#

btw, I think the input bug in 0.16 is somehow related to adding FrameInterpolate::<Transform> to an entity when it is being "moved" via input which changes the Position/LinearVelocity

pine cape
#

In 0.16 I don't think it's correct to use FrameInterpolate::<Transform>, you should directly use FrameInterpolate::<Position> and FrameInterpolate::<Rotation>

In 0.17 I'm adding custom systems to do the conversion from Position/Rotation to Transform, so that you can replicate Position but do FrameInterpolation/Correction with Transform if you want to

strong barn
#

Yes, that's exactly what I want to do.

My multiplayer game has physics, and as far as I understand, avian2d works best when you are manipulating the linearvelocity (as opposed to just teleport-moving objects +/- 20 pixels every input), but I need my transform to interpolate.

It's a bit hard to get to configure right because there's a lot of "gotchas" with this; need to disable the avian SyncPlugin, need to add FrameInterpolate::<Position> on the interpolated/predicts components, etc.

pine cape
#

I agree, it's super hard to get it right

strong barn
#

Oh, one more question I had for you, Periwink. I need my transform adjustments to propagate to children. Can you account for that with your new approach?

My entity for the player looks like this:

Player 
- Transform
- Position
- LinearVelocity
- Children:
  - (Avatar, Transform)
  - (Nametag, Transform)
  - (Shadow, Transform)

and those children have their own transforms relative to the player's position

pine cape
spice rapids
#

Not really familiar with that part - just re-enabling this right? If so, the result is the same on my end, still have the stuttering movement

pine cape
spice rapids
#

I'll try it on your branch. I wasn't using it because I thought avian wasn't ready for .17 yet

pine cape
#

No worries; it's not released but their main branch is on 0.17

#

i will also merge this branch to master today

spice rapids
#

oh nice, might just wait til then anyway

pine cape
pine cape
spice rapids
spice rapids
#

Possibly related:

pine cape
#

Even now? When I merged i introduced a bug on entity serde

#

I think i pushed a fix

spice rapids
#

just pulled, still bugged though behavior is slightly different. video shortly

#

When characters fly off screen it's happening even though no keys are being pressed anymore. No apparent difference on the input side for why it sometimes is "stuck" vs starts moving

pine cape
#

Sorry about that, will investigate later

#

I had tested this on my branch, but looks like i messed up something during the merge

spice rapids
#

no worries!

strong barn
#

--
Question 2: What does Rebroadcasting BEI inputs do?

pine cape
pine cape
pine cape
spice rapids
#

running it with three instances:

cargo run -- server
cargo run -- client -c 1
cargo run -- client -c 2
#

let me know if there's any other info that would help

pine cape
#

I'm using

rm -rf SL && cargo run --no-default-features --features=server,netcode,udp -- server 2>&1 | tee SL
rm -rf CL1 && cargo run --no-default-features --features=client,netcode,gui,udp -- client -c 1 2>&1 | tee CL1
rm -rf CL2 && cargo run --no-default-features --features=client,netcode,gui,udp -- client -c 2 2>&1 | tee CL2
#

I just see an issue where a client has a hard time pushing another, but otherwise things seem fine

rose quail
#

I seem to be running into something similar to #1189344685546811564 message on 0.24.2. Adding an seemingly unrelated observer causes the warn WARN aeronet_io::packet: 223v1 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer and causes no replication to happen.

This is fine:

// Observers
app.add_observer(handle_card_draw);
app.add_observer(add_card_visuals);
//app.add_observer(add_hand_visuals);

While this is not:

// Observers
app.add_observer(handle_card_draw);
app.add_observer(add_card_visuals);
app.add_observer(add_hand_visuals);

It doesn't seem like this observer needs to be related at all to replication:

#[derive(Event)]
pub struct Test;

pub fn add_hand_visuals(trigger: Trigger<Test>, mut commands: Commands) {
    println!("Something");
}

Very very strange. Any thoughts @pine cape?

final rain
#

Hi, i'm trying to selectively replicate components as the ComponentRegistry has many component I have registered for luau scripting on the server side.

In the lightyear book, I found the mention that

By default, every component in the entity that is part of the ComponentRegistry will be replicated.

However I did not find anywhere in the book on how to diverge from this default behavior, what would be the best practice to selectively send specific components in Lightyear?

Thanks in advance !

pine cape
pine cape
rose quail
final rain
unkempt sedge
#

@pine cape Mr Peri npcs who are only interpolated, how would you make them collidable with predicted entities?

silent patrol
#

Seems like bevy_web_keepalive is pulling Bevy 0.16 (on main)

rose quail
unique plover
#
fn fix_interpolation_system(
    mut interpolated_query: Query<(
        &mut Position,
        &mut Rotation,
        &mut LinearVelocity,
        &mut AngularVelocity,
        &Interpolated,
    )>,
    confirmed_query: Query<(
        &Position,
        &Rotation,
        &LinearVelocity,
        &AngularVelocity,
    ), (Without<Interpolated>, With<Confirmed>)>,
) {
    if confirmed_query.is_empty() {
        return;
    }

    for (
        mut position,
        mut rotation,
        mut linear_velocity,
        mut angular_velocity,
        &Interpolated { confirmed_entity },
    ) in interpolated_query.iter_mut() {
        let Ok((
            &confirmed_position,
            &confirmed_rotation,
            &confirmed_linear_velocity,
            &confirmed_angular_velocity,
        )) = confirmed_query.get(confirmed_entity) else {
            warn!("Unable to get confirmed position");
            continue;
        };

        *position = confirmed_position;
        *rotation = confirmed_rotation;
        *linear_velocity = confirmed_linear_velocity;
        *angular_velocity = confirmed_angular_velocity;
    }
}
#

You will almost certainly get desyncs when colliding, but it will be less frequently than not copying from confirmed. There's probably a better way to handle this, but like I said, I plan on ditching this approach for full prediction.

pine cape
unkempt sedge
final rain
timber grove
#

Anyone used both replicon and lightyear? Trying to weigh up which to use for my project. It seems that lightyear is more extensive (e.g. out of the box supporting prediction/interpolation), but are there any potential downsides compared to replicon that I should be aware of?

pine cape
#

replicaon has probably better documentation. I think it's also better optimized, but i don't think the CPU cost matters much unless you have a big project

silent patrol
silent patrol
#

were there any changes to webtransport in the 0.25 pre-release? my client is unable to connect to the server for some reason now
trace logs barely indicate anything problematic..

this is my client's output:

2025-10-09T09:42:56.531049Z DEBUG lightyear_netcode::client_plugin: Starting netcode connection process
2025-10-09T09:42:56.532418Z DEBUG lightyear_netcode::client: client state changing from Disconnected to SendingConnectionRequest
2025-10-09T09:42:56.537858Z  INFO lightyear_netcode::client: client connecting to server 192.168.88.22:5000 [1/1]
2025-10-09T09:42:56.542724Z TRACE lightyear_connection::client: Triggering LinkStart because Connect was triggered
2025-10-09T09:42:56.547592Z TRACE lightyear_aeronet: SessionEndpoint added on AeronetLink 111v1. Adding Linking on Link entity 99v0
2025-10-09T09:42:56.554658Z TRACE lightyear_aeronet: Session added on AeronetLink 111v1. Adding Linked on Link entity 99v0
2025-10-09T09:42:56.559281Z TRACE lightyear_aeronet: LocalAddr added on AeronetLink 111v1. Adding on Link entity 99v0
2025-10-09T09:42:56.565643Z TRACE lightyear_aeronet: PeerAddr added on AeronetLink 111v1. Adding on Link entity 99v0
2025-10-09T09:42:56.570051Z TRACE lightyear_aeronet: Received 0 packets
2025-10-09T09:42:56.575162Z DEBUG lightyear_netcode::client: client sending connection request packet to server
2025-10-09T09:42:56.580543Z TRACE lightyear_aeronet: Sending 1 packet
2025-10-09T09:42:56.587312Z TRACE lightyear_aeronet: Received 0 packets
2025-10-09T09:42:56.593969Z TRACE lightyear_aeronet: Sending 0 packet
2025-10-09T09:42:56.599579Z TRACE lightyear_sync::timeline::input: Recomputing input delay on config update! Input delay ticks: 0. Config: InputDelayConfig { minimum_input_delay_ticks: 0, maximum_input_delay_before_prediction: 0, maximum_predicted_ticks: 100 }
2025-10-09T09:42:56.606203Z TRACE lightyear_aeronet: Received 0 packets
2025-10-09T09:42:56.610923Z TRACE lightyear_aeronet: Sending 0 packet
2025-10-09T09:42:56.613688Z TRACE lightyear_sync::timeline::input: Recomputing input delay on config update! Input delay ticks: 0. Config: InputDelayConfig { minimum_input_delay_ticks: 0, maximum_input_delay_before_prediction: 0, maximum_predicted_ticks: 100 }

// spams these messages repeatedly with eventual resends with the "Sending 1 packet" log entry again

server:

2025-10-09T09:42:56.751756Z TRACE lightyear_aeronet: Session added on AeronetLink 108v0. Adding Linked on Link entity 109v0
2025-10-09T09:42:56.758364Z TRACE lightyear_aeronet: SessionEndpoint added on AeronetLink 108v0. Adding Linking on Link entity 109v0
2025-10-09T09:42:56.764349Z TRACE lightyear_aeronet: PeerAddr added on AeronetLink 108v0. Adding on Link entity 109v0
2025-10-09T09:43:05.870959Z TRACE lightyear_aeronet: Disconnected (reason: "Disconnected by remote: (dropped)") triggered added on AeronetLink 108v0. Adding Unlinked on Link entity 109v0
2025-10-09T09:43:05.877259Z TRACE lightyear_connection::client: Adding Disconnected because the link got Unlinked (reason: "Disconnected by remote: (dropped)")
#

don't know what the problem is, will try to reproduce this with the webtransport examples in the lightyear repo

#

Found this when trying to run the launcher example:

2025-10-09T09:58:09.420863Z  WARN aeronet_io::packet: 108v0 has 2 received packets which have not been consumed - this indicates a bug in code above the IO layer
2025-10-09T09:58:09.431380Z  WARN aeronet_io::packet: 108v0 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer
2025-10-09T09:58:09.531708Z  WARN aeronet_io::packet: 108v0 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer
2025-10-09T09:58:09.631489Z  WARN aeronet_io::packet: 108v0 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer
2025-10-09T09:58:09.732775Z  WARN aeronet_io::packet: 108v0 has 1 received packets which have not been consumed - this indicates a bug in code above the IO layer

after enabling aeronet_io logs in my app, I got the same messages. So seems like a bug on the lightyear end?..

silent patrol
#

or maybe aeronet itself

pine cape
#

Yeah aeronet is currently bugged, the author hasn't had the time to complete the upgrade

#

Only UDP works

silent patrol
#

webtransport works with this fix

silent patrol
#

Another issue that I noticed is that previously spawned entities don't get replicated to new clients. Haven't reproduced that on lightyear examples yet, so not sure where exactly the root issue is

silent patrol
#

well, I can neither reproduce this in lightyear, nor I understand what could be causing this issue in my app after updating to 0.25

silent patrol
#
commands.spawn((
    TestEnemy,
    Replicate::to_clients(NetworkTarget::All),
    PredictionTarget::to_clients(NetworkTarget::Single(player_id.client_id)),
    InterpolationTarget::to_clients(NetworkTarget::AllExceptSingle(player_id.client_id)),
    ControlledBy {
        owner: client_entity,
        lifetime: Default::default(),
    },
));

just tested this on a single marker component TestEnemy, and in my app the spawned entity just doesn't appear for clients that spawn later

pine cape
#

Thanks, will check. So this is spawned on server before clients spawn?

pine cape
#

How do you query for the entity on the client? Do you use Controlled? Maybe that's the issue

silent patrol
#

I query only for the TestEnemy component, without any additional filters

silent patrol
silent patrol
#

I believe you still have the access to my mine_crawler repo btw. 😁 If you want, I can help you navigate through it (and give the instructions to run it) to reproduce the issue

rose quail
# pine cape Very strange. Probably related to how trigger events are serialized? Do you have...

I have reduced the repo as much as I can: https://github.com/IRSMsoso/minimal-consumption-bug. I also have my full project at the point the issue happened in another branch full-project.
Removing anything else causes the warn to stop printing (and presumably replication to work again but I don't know for sure since I removed spawning anything and was just focused on keeping the warn). For example, removing the adding of the CardEffectsPlugin (which just adds a bunch of global observers) causes the warn to stop.
I'm using the aeronet_steam peer to peer through lobby invites. I could try a different aeronet setup but it would take me a while to understand how. Let me know if you need anything else. I'm happy to help try and figure it out, but my knowledge of the lightyear codebase is pretty minimal.

turbid marsh
#

This might be a novice question, but I couldnt find many examples of doing server authoritative movement while using client authoritative camera rotation in first person 3d. This is made more complicated since I'm sending my inputs through the leafwing integration.

There were no examples for controlling a player character in 3d on the lightyear page so I've had difficulties figuring this out.

unkempt sedge
#

there is also the fps example

turbid marsh
#

The fps example is in 2d

#

And its movement is relative to north/east, not relative to heading

turbid marsh
unkempt sedge
#

Bevy enhanced input

pine cape
# turbid marsh This might be a novice question, but I couldnt find many examples of doing serve...
GitHub

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

GitHub

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

turbid marsh
#

Oh thanks I get it now. I didnt make the connection with the bullets following the mouse

pine cape
# rose quail I have reduced the repo as much as I can: https://github.com/IRSMsoso/minimal-co...

I get

src/steamnetworkingsockets/clientlib/steamnetworkingsockets_lowlevel.cpp (2535) : Assertion Failed: Extra control data returned besides TOS?  0x0/0x1b
src/steamnetworkingsockets/clientlib/steamnetworkingsockets_lowlevel.cpp (2535) : Assertion Failed: Extra control data returned besides TOS?  0x0/0x1b
src/steamnetworkingsockets/clientlib/steamnetworkingsockets_lowlevel.cpp (2541) : No control data returned even though we asked for TOS?
src/steamnetworkingsockets/clientlib/steamnetworkingsockets_lowlevel.cpp (2541) : No control data returned even though we asked for TOS?

on mac; i will have to try without steam

turbid marsh
#

The problem with the avian one was that, since im doing a 3rd person orbit cam, I wanted specifically client authoritative camera object. The FPS example should help though

rose quail
final rain
unkempt sedge
#

@pine cape Sorry to bother mr peri but is there an event that get returned whenever a client fails to connect to server?

#

Something like I failed because x happened and so on

#

ah just noticed disconnected has reason

unique plover
# turbid marsh The problem with the avian one was that, since im doing a 3rd person orbit cam, ...

If you end up using BEI, the feature you're looking for is ActionMock, We use it to set the camera as a fake Vec3 input, so it's buffered for other players, but client authoritative.

Action:

#[derive(InputAction, Debug)]
#[action_output(Vec3)]
pub struct CameraForwardAction;

Components:

#[derive(Component, Default, Debug, Reflect, Serialize, Deserialize, Clone, PartialEq)]
#[reflect(Component)]
pub struct NetworkInputComponent;

#[derive(Component, Default, Debug, Reflect)]
#[reflect(Component)]
pub struct LocalNetworkedActionComponent;

Spawning the action:

context.spawn((
    Action::<CameraForwardAction>::new(),
    LocalNetworkedActionComponent, // needed to target since remote players will have
    // no bindings since this is mocked
    // Added to help lightyear pick up this action since it doesn't have bindings
    InputMarker::<NetworkInputComponent>::default(),
));

Setting the value on the client:

fn third_person_camera_input_system(
    transform: Single<&Transform, With<ThirdPersonCameraComponent>>,
    action: Single<Entity, (With<Action<CameraForwardAction>>, With<LocalNetworkedActionComponent>)>,
    mut commands: Commands,
) {
    commands.entity(*action).insert(ActionMock::new(ActionState::Fired, transform.forward().as_vec3(), MockSpan::Manual));
}
unique plover
# turbid marsh The problem with the avian one was that, since im doing a 3rd person orbit cam, ...

With Leafwing the key is action_state.set_axis_pair() which you set the value from your camera. You can also do set_axis_triple() if you want a full Vec3 like we did.

Here's it in action in the FPS example:
https://github.com/cBournhonesque/lightyear/blob/eff683e615db5706a0cb01afa83ac83da9d53547/examples/fps/src/client.rs#L47

GitHub

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

slow kernel
#

I did something similar I think. For BEI I have a local context (evaluated in PreUpdate) with a rotate action to update local camera for responsiveness and sets a mock actions values to the resulting yaw/pitch/roll in a separate networked context added with lightyear (evaluated in FixedPreUpdate I believe). It seems to work as desired

turbid marsh
unkempt sedge
#

@pine cape Hello again, I came here to annoy you once again. So as I was building my world previously to actually starting my server. I noticed a few warning telling me no server was found. I am guessing a little bit of the logic utilizes of the server component.

#

The trigger for this error seems to be whenever I add the replicate component unto an entity. And server is not properly started, the hooks gets triggered therefore running this method. Not sure if I should worry, as everything works normally

#

The client log also is hella annoying

#

Do you mind if I make them a little less noisy?

pine cape
#

Maybe you can set them to warn and then add lightyear_replication::send::components=error to your log filter?

unkempt sedge
pine cape
#

You can set the warning on lightyear; thanks

pine cape
#

Ty

#

I fixed most of the issues in the projectiles example, that's probably the most comprehensive example, that combines elements from rooms, avian, BEI, all possible replication modes, prespawning, etc.

pine cape
#

Next thing i'm trying to understand/fix is why on the avian-3d example, the remote player's movement is not entirely smooth, even though the player presses the same input (so it should be perfectly predicted). For the local player (controlling the entity) everything is smooth, so FrameInterpolation is working properly.

But for the remote entity it's not smooth, even though (from looking at logs), the FrameInterpolation should be working correctly

#

It looks like one player (player controlling the entity, where things are smooth) has a higher framerate than the other; this could be a hint.

#

Hm it looks like a non-focused window will have its frame rate reduced, which might be why the non-focused window has less-smooth movement

#

Aha that was it! Alright the avian_3d example is pretty smooth now

earnest fog
#

Is BEI input replication to other clients working? I tried the projectiles example with inputs only and the other clients were getting errors received input message for unrecognized entity

pine cape
#

Those errors should only appear at the beginning as things are still being replicated. If you can see the bot firing then the input replication is working

stray sinew
#

Is there a way to make it so that one replication group always gets replicated before another? I tried with just setting ones priority higher but it was not guaranteed.

#

What I'm trying to do is make a replicated hierarchy that replicates top to bottom in order. I've tried just making them all the same replication group but I get network lag when I do it like that.

pine cape
#

I don't think there's a way currently. I should add a way for a received replication message to remain buffered on the receiver until any parent is received

#

How big is the hierarchy? I'd be interested in knowing how many network packets the message is split into for it to cause lags

stray sinew
#

The hierarchy looks like this:
Galaxy Root -> 1 Star -> 1 Planet -> 4 Moons -> (8 player controlled entities, 30~ interpolated missiles, 100~ deterministic bullets)

All of that in a single ReplicationGroup produces visible lag, but splitting each type into its own ReplicationGroup runs smoothly

#

I think for now I'll add NetworkVisibility to all entities and replicate child entities after a delay.

pine cape
#

Sg!

#

I think with the avian release tomorrow all my deps will have upgraded, so I will probably do the release tomorrow evening as well

peak ice
#

Might be more of a rust question than a lightyear question, but do you think it’s faster to have one ProjectileMarker component with a projectiletype enum, or each projectile type have its own component while using lightyear?

For example when handling collisions a system only handling one type of projectile has to loop through all the projectiles and check if it is the right type, kinda makes me thing separate components is the way to go, but then I’d have ~50 components to replicate.

pine cape
#

How often do you need to iterate through only one type of projectile? That shouldn't happen too often no?

silent patrol
#
2025-10-13T13:38:17.962236Z  INFO mc_server_lib::tests: Frame step client_tick=Some(Tick(99)) server_tick=Tick(99)
2025-10-13T13:38:17.972616Z  INFO mc_server_lib::tests: Frame step client_tick=Some(Tick(100)) server_tick=Tick(100)
2025-10-13T13:38:17.972856Z ERROR lightyear_replication::send::components: ClientOf 117v0 not found or does not have ReplicationSender
2025-10-13T13:38:17.983139Z  INFO mc_server_lib::tests: Frame step client_tick=Some(Tick(101)) server_tick=Tick(101)
2025-10-13T13:38:17.993914Z  INFO mc_server_lib::tests: Frame step client_tick=Some(Tick(102)) server_tick=Tick(102)
test tests::test_spawn_new_connection ... FAILED
pine cape
#

What features are you using?

silent patrol
#

trying to do default-features = false everywhere, unless it doesn't compile

#

narrowing it down atm, which one is missing...

mortal stag
#

Hi. I need some help in getting prediction to work. I have done the setup in Protocol and server and have a movement prediction function but the entity is getting spawned without the Predicted component.
It has ShouldBePredicted though:

pine cape
mortal stag
#

Ahh thank you

silent patrol
silent patrol
#

ok, not sure what was wrong with the tests, but I finally found the issue: I re-inserted the ReplicationSender component on the Connected observer trigger too (which was previously added by the LinkOf observer)

#

so that was wrong. after removing that insertion, replication of previously spawned entities works again

pine cape
#

Yes adding it on LinkOf is more correct

#

If you add it on Connected, you will get issues with observers ordering (the replication-internal orderings also react to Connected to decide what to replicate, but only if the Link has ReplicationSender)

mortal stag
#

I sometimes get this warning on the client: Is this something to worry about?

2025-10-13T15:21:46.219714Z  WARN calloop::loop_logic: [calloop] Received an event for non-existence source: TokenInner { id: 3, version: 4, sub_id: 0 }
silent patrol
#

Is it possible to disable input sync per entity? I want to have some entities with leafwing input actions excluded from input synchronisation, because they don't exist on the server, yet clients attempt to broadcast input updates anyway and my server spams with "Failed to map entity" warning

(btw, I think I didn't see those warning before, until I updated to main)

peak ice
peak ice
pine cape
pine cape
pine cape
pine cape
#

Released the new version: #crates message

peak ice
#

Hey I'm trying to update to 0.17, and I'm switching to https://github.com/adrien-bon/bevy_ecs_tiled/blob/dev/examples/physics_avian_controller.rs instead of LDtK, I've come across a problem where the physics colliders are in the wrong position. When I remove

app.add_plugins(lightyear::avian2d::plugin::LightyearAvianPlugin {
            replication_mode: AvianReplicationMode::Position,
            ..default()
        });

They are in the correct position. I've tried adding a component to them which stores their transform when created and resetting their positions to those values later but that doesn't seem to work.

GitHub

Helpers for working with 2D tilemaps created with the Tiled map editor - adrien-bon/bevy_ecs_tiled

peak ice
#

never mind I think I figured something out

unkempt sedge
#

@pine cape Would you like for me to reopen the lag compensation pr?

#

I just saw your comment

unkempt sedge
#

@pine capeDoneeee~ now I have a teeny demand… follow me on GitHub, pwease~ hahahaha

peak ice
#

So the replication_mode: AvianReplicationMode::PositionButInterpolateTransform seems to be effecting everything I spawn, they are all "locked" to Vec2(0,0) unless I repeat apply position to a different value, this includes stuff from predicted players, interpolated enemies, and non-networked positions like world colliders. But once they are spawned in they can move around fine

pine cape
peak ice
#

yeah sure!

peak ice
#

So i tried only use AvianReplicationMode on the client and things seem to be working well

pine cape
#

Which mode?

peak ice
#

AvianReplicationMode::PositionButInterpolateTransform on just the client

#

and then no lightyearavianplugin on the server

tardy swallow
silent patrol
#

Is it possible to avoid despawning replicated entities on the client when it disconnects from the server?

#

ControlledBy lifetime seems to be affecting only the server

pine cape
#

We can add a setting for it, could you open an issue?

silent patrol
#

btw, do you imagine it as a setting? or maybe it could be a marker component that clients would add to entities for which they'd want this behaviour disabled?

silent patrol
pine cape
#

Yep it's there, I think a Lifetime component, or maybe Persistent marker component.
If Persistent is present, we don't despawn the Replicated entity

#

Or maybe we can add the Persistent component directly to the ReplicationReceiver...

#

so

  1. if Persistent is on ReplicationReceiver, we don't despawn any of the received entities
  2. if it's not, then we despawn any Replicated entities that don't have the Persistent marker component
silent patrol
#

I'm not a fan that it kinda has the same purpose as ControlledBy::lifetime, while one affects only clients, and another - only servers. Maybe it would make sense to merge them into a single separate component in the future

#

and I can already foresee the confusion that this marker isn't replicated and should be added manually both on client and server - some users might not expect that

as I was wondering whether ControlledBy::lifetime affects client behaviour or not

pine cape
#

Maybe, one is on the receiver side, the other on sender side. I'll probably rename them later

#

Ah I see what you're suggesting. That the sender specifies the behaviour for the receiver

silent patrol
marble wyvern
#

console spam on the server

#

starts as soon as client connects

#

what could be causing this?

#

i recreated the avian character example with BEI

#

and this

marble wyvern
#

when i connect multiple clients this happens

#

the clients only see their own player moving

#

the other one just desyncs

#

i think i messed something up a lot

pine cape
#

Maybe you can compare with the BEI example

marble wyvern
#

i added the inspector and the Client entities are there so i dont know why it says that

#

same with both player entities

#

The BEI example uses both prediction and interpolation while the Avian character one only does prediction

marble wyvern
#

i deleted everything

#

im using the example as a starting point now

fathom heath
#

Just updated to 0.25 and I'm getting an error when implementing Actionlike's input_control_kind function.
"CharacterAction does not implement 'FromReflect' so cannot be created through reflection."
Here is what I'm deriving for my CharacterAction enum, same as in 0.24 and as in the examples provided for 0.25
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect, Serialize, Deserialize)]

pine cape
peak ice
#

Hello again:) The new update is looking solid! actually got frame interpolation working for the first time! I have one question of replicating Position, Here's what is happening:

Server spawns enemy, enemy is in the correct position,
Client joins and spawns the same enemy, enemy is at Vec::ZERO,
I manually change the position on the server slightly, and now the enemies are in the correct position on both client and server.

Do Positions not send updates if they have not changed?

#

My second question with Position is do you think we could have a server based ChangePositionWithoutFrameInterpolation component, message, event, something? It would be useful for A) reloading levels/changing levels B) any form of blink ability.

I'm going to need one, but It seems like everyone with frame interpolation would need one so maybe it should be apart of lightyear?

pine cape
# peak ice Hello again:) The new update is looking solid! actually got frame interpolation ...

For your first question, is this for your interpolated entities?
This actually a subtle footgun, this is what's happening:

  • for Predicted entities, your Position is replicated as Confirmed<Position>. This triggers an immediate rollback on the client which inserts the correct Position. If you're not getting an immediate rollback then it's a bug
  • for Interpolated entities, it is possible that only one of Position or Rotation gets added (and not both at the same time). This can happen if Rotation doesn't get updated frequently for your entity. This can cause issues because the sync_pos_to_transform system from avian only does the sync from Pos/Rot -> Transform when BOTH are present on the same time. So you might be stuck with a Transform::default() for a short-while, until both Position/Rotation are present on the entity. For that reason it's best to add rendering components on interpolated entities only when BOTH Position and Rotation are present.
  • Lastly there is another subtle thing. Inserting RigidBody on an entity automatically inserts Position/Rotation/Transform on it. For that reason you do NOT want to add RigidBody on interpolated entities because it's going to display the entity at Transform::default() until the first interpolation updates are received

These footguns are a bit unfortunate, but I think the change of merging Predicted/Interpolated/Confirmed into one entity is still the way to go, it makes a lot of things easier

#

I think your second point is valid, i'll open an issue. We want to avoid interpolating in those cases. Maybe i'll just add a PauseFrameInterpolation marker component that users can temporary add, what do you think?
I can also add a setting on FrameInterpolation to not interpolate if the component changed by more than X amount

fathom heath
peak ice
#

I am actually disabling rotation syncing to save on networking on my interpolated enemies, I'll try enabling it! Thank you!!!! And I think merging interpolated/predicted/confirmed entities was also a great step, a lot less footguns:). And I would perfer the PauseFramInterpolation option, but up to you!

unkempt sedge
#

@pine cape The upgrades I did in my lag compensation pr are. They were in the old pr description, sorry to answer you here is just more convenient

  • Multiple colliders can be lag compensated at once (I dont recall exactly) but I believe that was not possible before hand. I used a relationship for that
  • We dont need to care about RC in avian as we do a slight prediction if we were to hit the aabb or not if so we spawn the collider at that given place in time (packet loss would not be accounted for AND I dont know how I would do so)
  • That means dynamic bodies like BF4 bullets with trajectories and so on can still be accounted
  • It also means it is technically more exact
peak ice
peak ice
silent patrol
#

my use-case is a bit weird. I do want replication of despawn events, but I just want to avoid the last despawn by lightyear

pine cape
pine cape
silent patrol
#

Yes and yes

#

I want to ignore the despawn only if it was caused by the disconnect

I know I can use Lifetime::Persistent on the server end, to keep the entity for longer. But it doesn't quite work for me, as I still want to despawn an entity on the server immediatelly, but later on the client end

peak ice
# pine cape Sure please have a go at it, I think it will be simple, especially the Pause ver...

Im a little confused as to where the smoothing is happening, I thought this would work but blinks still smoothly slide across the world:

#[derive(Component, PartialEq, Serialize, Deserialize, Clone, Debug, Reflect)]
pub struct SkipFrameInterpolation;


/// Currently we will only support components that are present in the protocol and have a SyncMetadata implementation
pub(crate) fn visual_interpolation<C: Component<Mutability = Mutable> + Clone + Debug>(
    time: Res<Time<Fixed>>,
    registry: Res<InterpolationRegistry>,
    timeline: Single<&LocalTimeline, With<Client>>,
    mut query: Query<(&mut C, &mut FrameInterpolate<C>, Option<&SkipFrameInterpolation>)>,
) {
    let kind = DebugName::type_name::<C>();
    let tick = timeline.now.tick;
    // TODO: how should we get the overstep? the LocalTimeline is only incremented during FixedUpdate so has an overstep of 0.0
    //  the InputTimeline seems to have an overstep, but it doesn't match the Time<Fixed> overstep
    let overstep = time.overstep_fraction();
    for (mut component, mut interpolate_status, skip_interpolation) in query.iter_mut() {

        
        if skip_interpolation.is_some() && interpolate_status.current_value.is_some() {
            *component = interpolate_status.current_value.clone().unwrap();
            interpolate_status.previous_value = interpolate_status.current_value.clone();
            continue;
        }
peak ice
pine cape
unkempt sedge
#

@pine cape Great simplifcation making the confirmed entity as the predicted/interpolated one

#

unfortunately that caused me 600 problems, but that is fine.

pine cape
#

it's an investment

unkempt sedge
# pine cape it's an investment

Sorry to bother just a migration question, now with the change on RemoteTrigger, I need to use MessageSender and observer that event on server side?

pine cape
#

RemoteTrigger didn't change much; it's just that the entity_event is now part of the trigger itself

unkempt sedge
pine cape
#

It's RemoteEvent now

#

To match with bevy's renames

peak ice
#

So I have a question about predicted replication on controlled entities. Right now I have abilities like dashes, rolls, etc, that change the players' movement speed. I tried having movement speed in a replicated component, but that leads to rubber banding when the speed changes from a low value to a high value or vis versa. I think I have it fixed by having the client send a movement speed value as an input, so the server uses the exact movement speed used on the client for that specific AWSD movement tick. Does this make sense? Is there a better way to fix this issue? Like a general tick buffered component for stuff like movement speed?

silent patrol
# pine cape You might be able to do this yourself. Add a system that listens to a client dis...

I've just tried that, but it seems that it breaks replication of all other newly spawned entities

So I had the following scenario:

  1. Client A disconnected, server removed Replicatingcomponent for entity A and despawned it
  2. Client B got the event that client A disconnected, and removed entity A on its own
  3. Client A reconnects, gets its entity spawned by server, it got replicated to clietn A but not for client B
  4. Client B died and got its entity despawned by replication (which got replicated to every client)
  5. Client B sent a message to restart
  6. Server spawned a new entity for client B, it appeared only for client A, but not for B
#

so basically every client, that was connected while some entity got Replicating component removed on the server end, gets bugged

silent patrol
silent patrol
#

@pine cape is prediction no longer possible for immutable components? Seems like the immutable method variants have been removed, and add_prediction still requires SyncComponent (which in turn requires a mutable component)

unkempt sedge
feral mirage
pine cape
pine cape
#

but now that Confirmed and Predicted entities are merged, there's no need for that. The immutable component would be added directly on the Predicted entity

silent patrol
#

aha, I see, thanks!

pine cape
unkempt sedge
#

Meaning predicted/confirmed entity will follow the same relationship structure built in server when it comes to ordering and so on

peak ice
pine cape
pine cape
unkempt sedge
#

Interesting with avian refactor on forces there is no longer a need to keep them synced very handy

silent patrol
#

@pine cape hi again. Thx for the merge! Btw, I've just noticed that I'm still receiving "Replicated Entity ... not found in entity_query" messages. And in that PR I've changed them from trace level to warn

It seemed to me that non-existent replicated entity shouldn't be contained in the replicated_entities list, but apparently there are still cases where that can happen. Maybe you'll want to change the log level back or look into why entities still remain in that list (in case that's not working as designed)

pine cape
#

you get this after pausing replication by removing Replicating?

unkempt sedge
#

@pine cape Do you know of any bugs resetting collider positions to 0?

#

hmm it seens adding a rigidbody static component resets position? I guess is this new onadd system

pine cape
#

Yes check the docstring at the start of lightyear_avian/plugin.rs

#

Inserting RigidBody adds Position::default

#

I assume you get this on your Interpolated entities?

unkempt sedge
#

Or even scaled up to bevy

silent patrol
#

I can't understand where entity_query of the replicate system gets its filter, but apparently we should also change With<Replicating> to Has<Replicating> and skip silently if it's got false

upd. oh, it's ReplicationSendPlugin, found it

unkempt sedge
unkempt sedge
#

@pine cape Could you point out how componentreplicationoverrides work? Do I insert it unto the entitiy that is the replication sender, or the entity that I want to control it is replication type? My case scenario is, i want to use transform as position manager for all entities except my player entity (in this case he should use posittion/rotation)

#

ah I figured it out

#

Add unto the entity pointing out the replication sender that controls that enttiy

hot cloud
pine cape
#

Are the props predicted/interpolated?

hot cloud
#

Neither. Strangely, it worked before I upgraded to the latest version of Lightyear. I added prediction to it and it works, but is this the best thing to do for an entity that will never move? (Apart from despawning once broken, for example)

hot cloud
#

Also, before using lightyear, each prop had a RigidBody::Static and two child entities for colliders.

  • One for player collision
  • One with a Sensor—usually larger than player collision—for breaking/opening interactions.
    Now, the children no longer follow the parent entity when it moves.

I thought about making them separate entities (without adding ChildOf(PropEntity)) and using Bevy's custom Relationships with a system to follow the parent, but perhaps there's a way to setup lightyear to replicate the desired behavior?

pine cape
#

I think it might be a bug in avian, I've think seen people mentioning it

stray sinew
#

Feature suggestion: Component-specific interpolation delay

Currently, all components share the same interpolation delay. Adding per-component delay settings would let high-frequency components interpolate with minimal latency, while letting low-frequency components use a longer delay to smooth over sparse updates without introducing unnecessary latency to other components.

This would likely pair well with component-specific replication frequency settings. For example, in my setup, I only update position velocity components once per second while coasting, and every tick (5 TPS) when actively moving. This behaviour may already be possible by splitting an entity's data into multiple ReplicationGroups with their own send_frequencies.

pine cape
#

It should be possible, but how would the component-specific delay be communicated? via a one-time message from the server to client?
And yes component-specific replication frequency might be possible as well.
I think those are more niche improvements though, since htey are mostly performance focused

north skiff
#

I'm a little confused (and possibly disappointed?) that from what I can tell there doesn't appear to be a way to use lightyear to make a connection without prior communication of a ConnectToken. With WebTransport, it appears to be possible (and secure?) to connect from a browser to an arbitrary server, given an address and certificate hash. I understand UDP on its own is not secure, but I believe the same is not true for WebTransport. So why?

#

Am I right in the assumption that there is "Netcode" and "Steam" based clients and that's it?

north skiff
#

According to issue #138 there used to be Authentication::RequestConnectToken. What happened to that?

north skiff
#

Okay, managed to find the commit that removed it and looks like it's just None except worded unfortunately? As in, you still require a separate "backend" / connection.

pine cape
#

I want to support Clients that don't use Netcode, I just didn't get around to it

#

Basically the IO layer currently adds Linked/Unlinked, and the connection layer (Steam/Netcode) currently adds Connected/Disconnected.
IO = transmitting bytes
Connection = long-running connection over the IO with a unique PeerId

We should let IOs act as a Connection, so we would have 3 connection layers: Steam / Netcode / Raw

north skiff
#

I've been trying to dig at this from a couple angles (stripping down netcode package, starting from the steam package) but I probably just don't have the know-how required. Is there anything I could do to make this happen?

main reef
#

hi, i just started messing with lightyear 0.25 and wondered if there was any physics based first person controller best practices examples

main reef
molten dagger
#

I'm trying to setup webtransport, but it's stuck on client connecting to server 127.0.0.1:8384 [1/1], does anyone know how to fix it?

Client spawned with:

commands.spawn((
  Client::default(),
  LocalAddr(client_addr),
  PeerAddr(server_addr),
  Link::new(None),
  ReplicationReceiver::default(),
  NetcodeClient::new(auth, NetcodeConfig::default())?,
  WebTransportClientIo {
    certificate_digest: String::new(),
  }
))
pine cape
#

On web or on local?
Do you have a code snippet?

molten dagger
#

on local machine

pine cape
molten dagger
#

My setup is mostly the same, I dont see where the issue could be.

Full client code:

use std::time::Duration;

use bevy::prelude::*;
use lightyear::{netcode::Key, prelude::{client::*, *}};
use reclipsis_shared::{FIXED_TIMESTEP_HZ, SharedPlugin};

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            ClientPlugins {
                tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
            },
            SharedPlugin,
        ))
        .add_systems(Startup, test_connect)
        .run();
}

fn test_connect(mut commands: Commands) -> Result {
    let server_addr = "127.0.0.1:8384".parse()?;
    let client_addr = "127.0.0.1:8385".parse()?;

    let auth = Authentication::Manual {
        server_addr,
        client_id: 0,
        private_key: Key::default(),
        protocol_id: 0,
    };
    let client = commands
        .spawn((
            Client::default(),
            LocalAddr(client_addr),
            PeerAddr(server_addr),
            Link::new(None),
            ReplicationReceiver::default(),
            NetcodeClient::new(auth, NetcodeConfig::default())?,
            WebTransportClientIo {
                certificate_digest: String::new(),
            },
        ))
        .id();
    commands.trigger(Connect { entity: client });

    Ok(())
}
pine cape
#

When you start the server, try using "0.0.0.0:8384" as the address.
(the client still refers to the server as "127.0.0.1:8384"

molten dagger
#

Even when server is listening to 0.0.0.0, client can't connect to 127.0.0.1 or 0.0.0.0

pine cape
#

What is your code to start the server

molten dagger
#
use std::time::Duration;

use bevy::{log::LogPlugin, mesh::MeshPlugin, prelude::*, scene::ScenePlugin};
use lightyear::prelude::{server::*, *};
use reclipsis_shared::{FIXED_TIMESTEP_HZ, SharedPlugin};

pub const SEND_INTERVAL: Duration = Duration::from_millis(100);

fn main() {
    App::new()
        .add_plugins((
            MinimalPlugins,
            AssetPlugin::default(),
            MeshPlugin::default(),
            ScenePlugin::default(),
            LogPlugin::default(),
            ServerPlugins {
                tick_duration: Duration::from_secs_f64(1.0 / FIXED_TIMESTEP_HZ),
            },
            SharedPlugin,
        ))
        .add_systems(Startup, setup)
        .add_observer(handle_new_client)
        .add_observer(handle_connected_client)
        .run();
}

fn setup(mut commands: Commands) -> Result {
    let server_addr = "0.0.0.0:8384".parse()?;

    let server = commands
        .spawn((
            NetcodeServer::new(NetcodeConfig::default()),
            LocalAddr(server_addr),
            WebTransportServerIo {
                certificate: Identity::self_signed(&["localhost", "127.0.0.1", "::1"])?,
            },
        ))
        .id();
    commands.trigger(Start { entity: server });

    Ok(())
}

fn handle_new_client() {
    info!("Got new client!");

    // ...
}

fn handle_connected_client() {
    // ...
}
north skiff
#

Firefox does not work for me, Chromium does.

#

Funnily enough, native server did see the connection, and it stays open until Firefox process closed.

molten dagger
#

Oh i just realized i actually have syncthing running on port 8384

north skiff
#

unsure if by local you mean native

molten dagger
#

I'm gonna try a different port

north skiff
#

hah whoops. did you accidentally just use the same port?

molten dagger
#

yeah

#

still doesn't work :/

pine cape
#

have you tried enabling the webtransport_dangerous_configuration feature?

#

i tried using webtransport instead of udp in the simple_setup example and it works for me

molten dagger
#

It worked!

#

Thanks for your help :)

pine cape
#

normally on the server-side you should have gotten some errors related to certificates

molten dagger
#

nope, it just didn't connect

#

maybe the code is waiting for a successful connection event that it never receives?

#

idk

pine cape
#

I get this on my side:

2025-10-25T19:54:54.368160Z DEBUG aeronet_webtransport::server::backend: Failed to accept session: ByError(failed to await session request

Caused by:
    connection aborted by peer: the cryptographic handshake failed: error 48: invalid peer certificate: UnknownIssuer

not sure

molten dagger
#

Bevy's LogPlugin only prints info logs and higher, so that's why I didn't see it

pine cape
#

Yeah that probably should be a warn/error level log

pine cape
# north skiff I've been trying to dig at this from a couple angles (stripping down netcode pac...

merged https://github.com/cBournhonesque/lightyear/pull/1271 which adds lightyear_raw_connection to use the IO layer directly as a connection

GitHub

Adds lightyear_raw_connection, another Connection layer similar to lightyear_netcode and lightyear_steam.
Clients marked a RawClient will just use their underlying IO (for example WebTransport) as ...

autumn furnace
#

is there an example for setting up an in-process server (but not host-server, separate app with crossbeam channel)

#

I remember that being a supported or even recommended thing, but can't seem to find the code in examples/common

pine cape
#
GitHub

Basic menu setup for separate mode and clients in Lightyear - SueHeir/lightyear-menu

GitHub

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

north skiff
#

Should events like Start be tuple-like or implement Entity::from so they can be used as such?

commands.spawn(...).trigger(Start); // or Start::from

Right now I have to do this, I think?

commands.spawn(...).trigger(|entity| Start { entity });
#

And I'm finding out I can't pre-emptively add Replicate to entities or else I get a warning "No Server found in the world"?

autumn furnace
#

anyone have an example for scanning for servers on the local network?

north skiff
molten dagger
#

Uhh, how do i make the player move in whichever direction they are facing in the avian_3d_character example

#

my code doesnt quite work

forces.apply_force(transform.rotation * required_acceleration * mass.value());
pine cape
unkempt sedge
unkempt sedge
#

(Replication still works if sender is active/connected, even if added previous to server connected status)

pine cape
#

Ah you're right, it's already handled

molten dagger
north skiff
unkempt sedge
molten dagger
unkempt sedge
#

It should still work perhaps you should check if is receiveing the predicted inputs.

unkempt sedge
pine cape
#

I can change them to debug.

molten dagger
#

not sure if im even rotating them correctly

unkempt sedge
#

In thesis you can just send the eulerot fields. And them reconstruct them in server side.

#

That is how I do it at least

molten dagger
#

well its set by my orbit camera system, so i only need the yaw to be set

#

alr fixed it

north skiff
#

Looks like adding Start to RawServer does not actually trigger LinkStart and thus doesn't start the server? (I got around it by calling LinkStart manually.)

pine cape
unkempt sedge
north skiff
#

I also just noticed that the app is crashing because the PeerMetadata resource is missing.

#

They get added by the NetcodeServerPlugin from the looks of it.

#

Unsure if they should be moved to ServerPlugins or something else.

pine cape
north skiff
#

Not sure what you mean, they are in the connection plugin, but they are not registered since I'm not using NetcodeServerPlugin, which adds the ConnectionPlugins (of which there are three, actually?). So I've just added them manually. Unsure how this is meant to be done.

#

Got replication working though. Nice!

#

Predictive spawning is possible. What about predictive despawning?

unkempt sedge
# north skiff Predictive spawning is possible. What about predictive despawning?
north skiff
#

Sweet.

unkempt sedge
#

@pine cape Okey dokey, I added a just doc command so you can also check quickly if your docs are compilable in the ci

unkempt sedge
#

@pine cape The testing crate still requires 0.16?

craggy veldt
#

so does lightyear_examples_common?!?!

stray sinew
#

Despawning an entity that has NetworkVisibility and is non-visible to the client causes console spam.

How to reproduce:

  • Sever spawns an entity with NetworkVisibility and Replicate set to NetworkTarget::All
  • Client connects to server
  • Server despawns entity without it ever being visible to the client.
  • Logs get spammed

Other than the spam nothing seems to break.

north skiff
#

Unsure how to "connect" a local/HostClient. Keep getting a "ClientOf 114v0 not found or does not have ReplicationSender" error. I feel like the examples imply that you just add a LinkOf component, and then presumably treat it as any other client? By triggering a Connect event for it?

autumn furnace
#

is there anything in lightyear to sync the server and client Time<Virtual> ?

pine cape
pine cape
north skiff
pine cape
#

have you tried running the example in HostClient mode?

north skiff
#

Other than not being able to control the HostClient, I guess it works.

#

But it's slightly different since it's Netcode based, right?

#

Also, how would one connect to a hostname via WebTransport on web? Can't use ToSocketAddrs on web, after all, right?

pine cape
pine cape
north skiff
#

Well, with lightyear don't you have to specify a SocketAddr in PeerAddr?

pine cape
#

ah yes. That doesn't require std though

unkempt sedge
north skiff
#

And I appear to be getting the same issues after updating lightyear to latest.

autumn furnace
#

Is there a quick way to have a observer only run if there is a server and it's connected?

#

the only run condition seems to be is_host_server

pine cape
craggy veldt
north skiff
#

I switched out netcode for raw_connection in the simple_box example, and yeah, it seems like it works? There's some oddities with the example like being unable to move (host-client) and labels not updating as expected, but yeh.

#

(The examples are really difficult to use as a reference due to being so fragmented tho.)

north skiff
#

I think part of my problem was that I needed the server to be Started before I could connect my host-client to it. Thought that would be guaranteed by queuing the commands up in the right order but no.

unkempt sedge
#

ComponentReplicationOverride is a sample

cloud patio
#

Having an issue with specifying the spawn location of my player with Transform - I create a player with Transform::from_xyz(spawn_x, terrain_height + 20.0, spawn_z), on the server On<Add, Connected> but it always spawns in with Mut(Transform { translation: Vec3(0.0, 0.0, 0.0), rotation: Quat(0.0, 0.0, 0.0, 1.0), scale: Vec3(1.0, 1.0, 1.0) }) on the client and the server. I'm using AvianReplicationMode::Transform

unkempt sedge
#

@pine cape oi you got a laicense to thumbs up me

stray sinew
pine cape
unkempt sedge
#

Question, adding replicate like to a predicted entity will make it so the other entities will also have predicted components?

cloud patio
pine cape
unkempt sedge
stray sinew
unkempt sedge
#

hmm I guess in thesis they wont have the predicted component so nothing will happen

cloud patio
north skiff
#

was just wondering if it helps in your case or not

#

I'm guessing if there's some shenanigans and Transform is inserted before the command is applied, it could override it (with insert). That was my thinking. (In case anyone was wondering.)

#

(though I'm not sure if such shenanigans did or even could occur)

cloud patio
#

Oh I'm still debugging my issue but if i turn off the lightyear avian plugin and mark position and transform as replicated then the player spawns at the correct location and doesn't fall through the ground. I'm trying to figure out exactly what is causing the Transform to be overwritten and it wasn't that particular line

#

(I added an info! statement to that fn that was never printed)

north skiff
#

Is a debug breakpoint on a On<Add, Transform> (on the relevant entity only) possible? (or helpful at all, I haven't done a lot of debugging with Bevy)

cloud patio
#

I didn't think it would be helpful at finding exactly what is adding the Transform but I'll investigate further

pine cape
#

RigidBody adds Transform as well, if that's helpful. I haven't tested the ReplicationMode::Transform, i should add unit tests for it

cloud patio
#

Oh btw it did fix my issue, I just wasn't applying the patch correctly lmao

pine cape
unkempt sedge
#

@pine cape This is ugly

#
fn replicate_body_part(
    query: Populated<(Entity, &BodyPart), Without<ReplicateLike>>,
    replicated_like: Query<&ReplicateLike>,
    mut commands: Commands,
) {
    for (entity, body_part) in query.iter() {
        let body = body_part.get();
        debug!("{},{}", body, entity);
        if replicated_like.contains(body) {
            commands.entity(entity).insert((
                ReplicateLike { root: body },
                PredictionTarget::manual(vec![]),
                InterpolationTarget::manual(vec![]),
            ));
            debug!("Added replicate like to {} according to {}", entity, body);
        }
    }
}```
What do you say if we add an option to only replicatelike visibility?
unique plover
#

@pine cape Any chance you would know why HostClient Inputs are throwing this error on a connected client with rebroadcasted inputs? (on v0.25)

2025-10-28T15:50:05.073529Z  WARN bevy_enhanced_input::action::fns: action `CameraForwardAction` (`362v0`) expects `Axis3D`, but got `Bool`
2025-10-28T15:50:05.073541Z  WARN bevy_enhanced_input::action::fns: action `MoveAction` (`364v0`) expects `Axis2D`, but got `Bool`

I also see mapping errors for the host actions on the connected client initially, but then the errors go away when the BEI errors show up.

Additionally I'm seeing mapping errors on a second connected client. I think it's failing to get the other client's actions.

2025-10-28T15:53:36.304351Z  WARN lightyear_serde::entity_map: Failed to map entity 552v1
2025-10-28T15:53:36.304362Z  WARN lightyear_serde::entity_map: Failed to map entity 554v292
2025-10-28T15:53:36.304366Z  WARN lightyear_serde::entity_map: Failed to map entity 515v1
2025-10-28T15:53:36.304369Z  WARN lightyear_serde::entity_map: Failed to map entity 516v1
2025-10-28T15:53:36.304372Z  WARN lightyear_serde::entity_map: Failed to map entity 553v12
2025-10-28T15:53:36.304449Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Bool(false), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } end_tick=Tick(21374)
2025-10-28T15:53:36.304469Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Bool(false), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } end_tick=Tick(21374)
2025-10-28T15:53:36.304479Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequence { start_state: ActionsSnapshot { state: Fired, value: Bool(false), time: ActionTime { elapsed_secs: 10.265625, fired_secs: 10.265625 }, events: ActionEvents(4) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } end_tick=Tick(21374)
2025-10-28T15:53:36.304489Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } end_tick=Tick(21374)
pine cape
#

Normally you can ignore these errors

pine cape
unique plover
#

Should it only be happening initially?

pine cape
#

Yes, have you tried it in the BEI example?

unique plover
#

I have a feeling I'm missing something, especially since the second client is throwing continuous mapping errors.

#

I'll have to get set up with the example again, but I'm trying to diff the several different examples (projectiles, BEI, avian 3d character) against our code base to figure out what's missing. for example I added the LightyearAvianPlugin and added the ActionOf to type registration.

unique plover
#

All you would need to do to replicate is launch a host-client and attach 2 additional clients.

unkempt sedge
#

@pine cape Do you disable transform propagation for child entities of predicted physical entities?

unique plover
#

@pine cape Also the projectiles example has errors right away when running in HostClient mode

#

That's at tag 0.25.3, lemme check main.

#

Yeah, same on main.

north skiff
#

Are you supposed to manually start a local server (with no io/connection layer) by just adding the Started component? That's what I'm doing and it seems to work, but using the Start event doesn't seem to do anything.

pine cape
pine cape
pine cape
pine cape
# unique plover I have a feeling I'm missing something, especially since the second client is th...

I just tested in the BEI example and host-client input replication is working properly. An easy way to see if it's working is to check the number of rollbacks in the PredictionMetrics resource. If the inputs were not replicated, the client would always be rolling back whenever the host client is moving.

For instance if you set the input delay to 0 ticks, you can see that there are rollbacks despite input rebroadcast, because the client doesn't receive the host's inputs in time to use them for prediction

unique plover
#

It does work fine with HostClient and a single client.

#

This is essentially the inverse of the problem before 0.25. Before then, clients would get eachother's actions, but wouldn't get the host's. Now clients get the host's actions, but not other clients.

#

I'm specifically saying actions as in the Action entity is not replicated from other clients, input rebroadcasting seems to be fine (hence the errors in the log).

slow kernel
#

upgrading to 0.25 was pretty easy and prespawned seems to work like I hoped now 🎉

north skiff
#

I was wrong by the way. I don't think it's possible to start up a server without io. It appears to start up and then immediately shut down afterwards.

#

(Well, my "workaround" did.)

#

inserting (Linked, Started) works!

#

Presumably because theres some code that checks for Unlinked, Started and then transitions the server to Stopped.

north skiff
#

Hmm now my native client doesn't want to connect. (web client connects fine)

north skiff
#

"invalid remote address: [::1]:13580" hmmm

#

alright, it works with ipv4

#

so localhost resolves to ipv6

#

Which I think means.. if ToSocketAddrs returns both you should probably prefer IPv4 results, because those can always be upgraded to IPv6?

north skiff
#

Now I'm stuck with inputs. buffer_input isn't called. I've followed the tutorial, not yet doing any prediction, but I wonder if I'm missing anything.

#

Not entirely sure what causes InputMarker to be inserted, if I need to do it myself, or which entity would even be the correct one.

cloud patio
#

@pine cape why did u switch the all the examples to use webtransport by default instead of udp

unkempt sedge
#

@pine cape Will the replicated child entities components arrive at the same time as parent. If they are in the same replication group?

#

I ask because I ran into some issue here

pine cape
pine cape
pine cape
north skiff
unkempt sedge
pine cape
#

And then add the input systems only on the server

unkempt sedge
#

@pine cape Question if client is not the replicationsender, is there an efficient method to first mutate on client and them inform server. Besides events and so on? Or do I just make it so he is a replicationsender

pine cape
#

Either you use messages, or inputs, or ReplicationSender

unkempt sedge
pine cape
#

I only mutate if the component is already present on the receiver side; but I could also just re-insert

#

maybe this can be added in the ReplicationConfig

unkempt sedge
north skiff
#

I'm still really confused. Trying to wrap my head around some of the examples. InputPlugin::<Inputs> is being registered, but I can't for the life of me figure out what causes ActionState and ActionMarker to be added.

#

ActionMarker appears to get added to predicted players but it already requires there to be an ActionState.

#

I only see it being added explicitly in the spaceships demo.

#

(or the search on GitHub is being weird)

#

There's confusing comments, too.

Add an ActionState component on the Client entity to send inputs to the server
but the function does no such thing (explicitly)

#

Maybe the non-native input implementations (leafwing, BEI) do it automatically, but then I'm still curious how it decides which entity to add the input components to.

molten dagger
#

Could someone help, when a second client joins, no changes from one client replicate to the other.
I'm building a game on top of the avian_3d_character example.

I logged the transforms of characters in the apply_character_actions function, and each client only prints out their own transform (even though rebroadcast_inputs is set to true), while the server prints out both character's correct transforms.

#

replication_mode is set to transform

north skiff
#

Or... not? It creates a PredictionTarget for the owning client (plus comment) here but that ends up unused, and it inserts PredictionTarget::to_clients(NetworkTarget::All) later.

unkempt sedge
#

@pine cape Hmm are you disabling sleeping plugin of avian? Is that still necessary? I question becauser the flickering on debug mode annoys me

lyric badge
#

i cant connect to my remote server but im not sure why, server is setup on UNSPECIFIED, and the client goes to the public ip and same port but it just says its an unreachable network, but when i try it on my own pc i can connect to LOCALHOST without issue, any clues as to why this could be happening?

north skiff
pine cape
nimble hill
#

Hello, may I ask what is the correct way to implement multi connection mentioned in the book ? I tried having multiple Servers with separate IO components, but this seems to stop replication using ReplicationMode::SingleServer. Having multiple IO components in a single server does not seem to be possible since they each need a different LocalAddr.

pine cape
pine cape
# nimble hill Hello, may I ask what is the correct way to implement multi connection mentioned...

If you use multi server entities; you cannot use ReplicationMode::SingleServer, you need to explicitly specify on which server you're doing the replication.
But multi-server entities is not fully supported, a lot of systems only work with a single-server.

What kind of multiple servers do you need?
What is possible right now though is to have a single Server entity that has both SteamServer and NetcodeServer at the same time

nimble hill
#

It's not a must. If there isn't a simple way for this I'm fine with just using one type of connection.

pine cape
#

Yes this is a usecase that should be supported (and was in the past).
I think the solution would be to use ReplicationMode::Target, which should figure out a mapping from the PeerId to the Server entity corresponding to that peer

nimble hill
north skiff
pine cape
north skiff
#

I don't see it checking for ControlledBy or anything.

#

(but even then, I suppose it could insert any registered input onto an entity that isn't supposed to have that type of input present?)

unkempt sedge
#

Ngl, inserting components not triggering on insert events but triggering on remove is a little weird.

pine cape
north skiff
#

Would in not make sense to explicitly add the right input component and automatically check for it and whether ControlledBy was present on the target entity and pointing to the client? Or what is the purpose of ControlledBy?

#

And where would the validation layer go?

pine cape
#

on the server

north skiff
#

Am I supposed to write my own receive_input_message-like system?

pine cape
#

no, it's handled for you

north skiff
#

I can't find anything. How would I go about that?

pine cape
north skiff
#

I can't check for modifications on ActionState itself because I won't know which client they came from anymore.

#

I'd have to get between the message being received and the change being applied, no?

pine cape
#

You can attach a component on the entity that contains the PeerId; or you can use the ControlledBy entity

north skiff
#

Honestly, a rather small modification to receive_input_message would do exactly what I'd want.

  • Just before here I would check if the entity has a ControlledBy component and if owner is the client who sent the message, otherwise reject it with a warning.
  • Modify the query and require an InputState component to be present on the entity. The existence of that component would mean that it's supposed be able to handle and process that input. Without it, the client did something it wasn't supposed to. (This would require changing the examples and documentation to match.)
#

I can open an issue if you think that's reasonable.

pine cape
#

I don't really want to make ControlledBy required for Inputs, as the entire notion of 'control' is optional.
Also you would require users to add an InputState component on the receiver-side? that's also a pretty big ergonomic hit.

In production you would need to have a more involved input-validation layer/system that would go further than simply checking for some components being present

#

I don't see what problems you're facing right now. Are you worried about cheating?

north skiff
#

I'm not sure I'm using the term correctly but it just feels unsound.

#

Clients sending arbitrary input to arbitrary entities is just something that shouldn't be happening in my view.

#

And there should be an easy-to-use contract that is enforced. And the building blocks are already there.

#

I've been digging through the code for 3 days trying to figure out "how is the server defining that this entity is supposed to receive input?" and in the end I was perplexed that in fact, it did not at all.

harsh crescent
#

It's my understanding that, on the server, an entity needs to have something like PredictionTarget pointing at a client, in order for the server to listen to the actionstate component updates coming from that client, for that entity. I've just figured this out through trial and error, but I might be wrong. Also, I'm using Leafwing, not sure if this applies outside of that

pine cape
#

yes there is no input validation currently because i don't think it's something that's important enough to be deal with right now.
If someone has a live game with users where there is a potential issue of users cheating by sending inputs for the wrong entity, then we can focus more on that

north skiff
pine cape
unkempt sedge
harsh crescent
pine cape
# unkempt sedge Input validation is quite easy no? If a server get the action triggers and check...

yeah, i think the solution would be to put a hook where the user can provide their own function to validate the input and check if it should be rejected or not.
Then the user could add whatever logic that they want. I would accept a PR that does that.
Aeronet does something similar to accept incoming requests: https://github.com/aecsocket/aeronet/blob/main/crates/aeronet_websocket/src/server/config.rs#L23
But the function only has access to a limited amount of data. Ideally that system could fetch any ECS data.

GitHub

Set of Bevy-native networking crates, focused on providing robust and rock-solid data transfer primitives - aecsocket/aeronet

north skiff
#

Input validation to me is what happens in the shared function that applies the input. This is more like ... validating that the input message is received by the correct entity. (Which I still don't know how to do.)

#

Heck, even better. Some input validation should be done when the type is being constructed in the first place. "Parse, don't validate."

pine cape
#

yes it's not possible to do right now. The direction I want to take this is to let users define their own validation hooks

north skiff
#

What's the purpose of ControlledBy?

#

The documentation suggests that it lets a sender "control" the entity.

#

Is it only to replicate a Controlled component?

#

(and, well, despawn it automatically, if requested)

pine cape
#

The main purpose is:

  • add a Controlled component so that clients can easily filter for entities they 'control'
  • when a client disconnects, automatically despawn their controlled entities
north skiff
#

The naming and wording, while not explicit, does imply that it could have a hand in how input is being handled. At least to me. Perhaps OwnedBy and Owned would be clearer, if that was the intention. (Unsure if that's already in use anywhere.)

#

Also, lightyear claims it is "server-authoritative". What is authoritative about letting clients arbitrarily send input to any entities by default? When would you ever want that if you're building or using a library that makes such a claim?

#

I'll make an issue for now.

north skiff
#

Wait a moment, there was something I was confused about previously.

#

Some of the examples use an enum for their input types, right? Does that mean it's not possible to both Move and Shoot at the same time? And how does it represent no input?

unkempt sedge
north skiff
#

I'm using a struct in my case but I'm using the native input system.

#

Okay yeah leafwing is doing a hashmap behind the scenes.

#

I think I understand, then.

pine cape
pine cape
# unique plover Is this with a host + 2 clients? On tag 0.25.3 and main the clients do not get t...

this should solve the BEI input rebroadcast issues. https://github.com/cBournhonesque/lightyear/pull/1286
I tested with a host + 2 clients

GitHub

Fixes 3 important issues related to input rebroadcasting:

for BEi, we handled rebroadcasting inputs for entities controlled by the host-client. However in a host-client scenario, inputs from non-h...

unique plover
inner hill
#

seems the distributed authority example is broken rn btw

pine cape
#

Yep it hasn't been updated. I think authority in general needs more work

north skiff
#

I wonder, is there a good reason why client and server have different systems that run the same shared movement function? Why not register this once, say in the protocol plugin?

pub fn movement(
    time: Res<Time<Fixed>>,
    mut players: Query<
        (&mut Transform, &ActionState<Inputs>),
        // Must be a `Player` which is either be `ControlledBy` a remote
        // client (server-side) or its movement `Predicted` on the client.
        (With<Player>, Or<(With<ControlledBy>, With<Predicted>)>),
    >,
) {
pine cape
#

Yeah in some cases it should be possible to have a shared system for both client/server

unkempt sedge
#

@pine cape Was there recently a solved bug when it comes to entity maps? I ask because it seens everytime I add a mapable component via observer for a certain scenario of mine it seens to not be able to map. It is oddly specific so I am not gonna go into the details just curious

#

Is nothing too worrying it just fails to map one very specific replicated entity

pine cape
#

Nothing changed; maybe you need to use add_component_map_entities instead of add_map_entities if the map entities implementation comes from the Component trait instead of the MapEntities trait

unkempt sedge
#

@pine cape Ought oif curiosity why we only try to map anm entity for a few frames and not continously?

#

Btw this bug is truly insane, I can show you my pseudo fix if you like is quite fun, in a vc

#

In summary, when I make an entity. In an observer, that has a component that should point to another mapped entity. It does not find in remote map

#

But if I add on a system in any step of the way on insertion it does

#

well this destroyed my sunday

peak ice
#

how does the MissingDeltaFns error get fixed? Do specific types of deltafunction need written for each component?

#

also is there a downside to use it on pretty much everything?

peak ice
#

oh I see the example, you need to impl Diffable

shrewd granite
#

I need to send a ConnectToken and the server address + port together to be able to create a valid Client? The ConnectToken can be valid for multiple addresses if I'm reading this correctly?

pine cape
#

Yes correct

unkempt sedge
#

@pine cape I might be misimplementing replication groups, would you kindly give me a short step by step on how to do so? I am just adding the ReplicationGroup component unto my entity. Is there anything else? btw replication groups seems to not use replication groupos

pine cape
#

You just add ReplicationGroup on an entity; all entities that have the same ReplicationGroup are replicated together

unkempt sedge
#

@pine cape Found out what was the issue with map entities, if you map an entity previous to it being replicated. It fails to map it and since we dont continously attempt at mapping well it dies

#

What is your take on this, want me to make a pr fixing this interaction?

#

Also is there an issue already opened it seens map entities has been a theme of discussion on the repo issues

pine cape
#

I guess I'd like to know more about the usecase. How come you're mapping an entity before it is replicated?

cloud patio
#

any idea what's causing this? do i need to set a retention period for history?

unkempt sedge
pine cape
pine cape
cloud patio
#

nah i only replicate the players when they join but this graph was on the server overnight when i wasn't connecting any players

unkempt sedge
#

I added ReplicateLike

pine cape
north skiff
#

What's the appropriate way to synchronize first-person camera orientation?

unkempt sedge
north skiff
#

Well you wouldn't want your camera to jitter because of network issues or anything, so I'm thinking it should be entirely client-authoritative. But I'm not sure how to go about that. Is camera movement an input? Is it relative (mouse movement) or absolute (yaw, pitch)? Or should it avoid relying on the input system altogether?

#

The only place I can see camera orientation affecting gameplay, other than visually (player head rotation), is when placing things in the world, it might affect the placed object's rotation. That could in theory be encoded in the action tho.

unkempt sedge
north skiff
#

@unkempt sedge Where? From what I can tell, all the important stuff runs in (Pre)FixedUpdate. How would that even translate to responsive first-person camera controls?

unkempt sedge
#

Well in summary, you translate the given rotation from client (the rotation input) in server. Server only redo the action, but can still check if is absurd

north skiff
#

The example still fails to really show how it would be done in practice. Like, do you apply the change from AccumulatedMouseMotion to the camera orientation directly in Update and also accumulate it so it can be sent at as input action in FixedUpdate?

unkempt sedge
#

@pine cape Made a mre in the examples crate here, just run the just commandjust mre https://github.com/Baker-games/psycho_duel. Btw I did this to have a very quick mannerism to show the weird issue I come up while using your crate

inner hill
#

Does it make any sense to have Server and Client on the same entity if you are just doing a host client?

#

I'm assuming it probably shouldn't make a difference, just makes it a bit easier to do some bookkeeping

pine cape
pine cape
pine cape
unkempt sedge
silent patrol
#

Upd. sorry, disregard this

@pine cape hi! another quirk I found with doing .remove::<Replicating>().despawn() is that a server will try to replicate removal of components. It's not replicating the despawn, but I guess since despawning also means removing the components, somehow the absent Replicating isn't enough to prevent the server from sending remove entity actions

I extended the log statement a bit to debug-print the actions:

// safety: we know by this point that the entity exists
let Some(local_entity_mut) = remote_entity_map.get_by_remote(world, entity) else {
    error!(?entity, ?actions, "cannot find entity");
    continue;
};

and I'm getting the following:

2025-11-06T23:14:05.625194Z ERROR lightyear_replication::receive: cannot find entity entity=108v1 actions=EntityActions { spawn: None, insert: [], remove: [2, 3, 4, 6, 7, 8], updates: [] }
#

Oh, nevermind, my bad, I actually broke the code in my lightyear fork, the main branch is good

nimble hill
#

Regarding the room system, it seems that when an entity is removed from a room, the clients in that room always lose visibility to it. Wouldn't this be a problem when a client and an entity share multiple rooms?

pine cape
nimble hill
cloud patio
#

If I want a user to be able to connect to different servers is it best to despawn the client on disconnect and create a new one when the user switches to a new connection?

pine cape
earnest fog
pine cape
#

Hm no it means that something has gone very wrong. It compares checksum at ticks that were already received and processed by all clients, so it's mostly to catch non-determinism bugs

unkempt sedge
#

@pine cape If it isnt much trouble would you make a release introducing avian 0.4.1, I can open the PR if you like. (A hook bug broke my collider e.e)

pine cape
#

I can do it sometime this weekend

fathom heath
#

If I'm using CrossbeamIO for Client & Server app spawned in a separate thread, do I still need to trigger Connect on the client?
I have a set up which works for Client connecting to local Server using UdpIO, but the same configuration doesn't work if I swap out for CrossbeamIO components - when the Client tries to connect it times out.

pine cape
#

Yes you still need to trigger Connect

#

I use crossbeam in tests; maybe you can take a look

unique plover
# pine cape this should solve the BEI input rebroadcast issues. https://github.com/cBournhon...

I've been using this for a bit and it looks like client to client is just fine now.

I am, however, noticing a difference with Host inputs. I don't have a repro on an example yet, but maybe you'll have an idea what's going on? Basically, the host looks like it's potentially missing some inputs or missing when the inputs end. For example, my player controller will repeatedly look like it's trying to continue moving in the direction the host was moving before they stopped for a little bit. Other clients are flawless, it only affects the hostclient on clients. It's the exact same code path for my systems for both regular client and hostclient for the player controller and input handling systems, so I'm leaning towards it being BEI/Lightyear.

#

In this video the first client I'm controlling in the bottom left corner has smooth animations, smooth turning, stops when the input stops, etc. However, when I start moving the HostClient around (top right) you can see on the clients the turning is jittery, there's continued movement and corrections when the player stops.

fathom heath
# pine cape Yes you still need to trigger Connect

I got it working, had assumed that the LinkOf entity would be created on the server automatically when a client connects with CrossbeamIo as ServerUdpIo seemed to do, but examples created it manually so I did that.

inner hill
#

are custom messages still a thing in the latest lightyear?

#

trying to send some voxel command (set sdf to X voxel, send chunk, etc.) stuff over the network on an unordered reliable channel, but having a bit of trouble figuring that out

pine cape
inner hill
#

ooo thank you!

unique plover
pine cape
#

i would always add link conditioning as testing with no latency is not realistic.

peak ice
#

So I've been trying to solve this jitter on my characters when they dash, it only seems to happen when the character speed changes, and one fix I've found that works is sending the player's speed from the client as an input, and using that input to decide how fast the server moves the character. But I don't like this solution for authoritative-server reasons.

Could I get some information how leafwings Action state works in reguard to input delay?

Right now I have this on the client

InputTimeline(Timeline::from(
                Input::default() 
                    .with_input_delay(InputDelayConfig { minimum_input_delay_ticks: 7, maximum_input_delay_before_prediction: 10, maximum_predicted_ticks: 0 }),
            )),

and am using the following to see the actions of the player on both client and server

&ActionState<PlayerActions>,

One thing I noticed is that movement speed updated directly happens before the movement speed over input changes, and I think this is the cause but I don't know what I should do to fix it

unkempt sedge
#

I am guessing that is what causes that jitter (rollback)

pine cape
#

What part is the jitter? can you also attach what it looks like on the main client to see what it's supposed to look like without networking?
Do you have prediction enabled for the velocity?

InputDelay just means that the input is sent immediately to other players, but becomes active locally with some delay, so it helps removes some of the latency for predictioon purposes.

peak ice
peak ice
pine cape
#

Inputs are put in a buffer and are applied only on the correct tick.
I still dont see in your video at which point there is an issue. What is the exact timestamp?

peak ice
#

look at 0:06 to 0:07

#

this is when I take the player speed on the client and buffer it into the inputs and use that :

 app.add_systems(
            FixedPreUpdate,
            update_current_move_speed
                .before(InputSystems::BufferClientInputs)
                .in_set(InputManagerSystem::ManualControl),
        );

pub fn update_current_move_speed(
    mut action_state_query: Query<
        (&PlayerSharedMovement, &mut ActionState<PlayerActions>),
        (With<Predicted>, With<Controlled>),
    >,
) {
    for (movement, mut action_state) in action_state_query.iter_mut() {
        action_state.set_value(&PlayerActions::MoveSpeed, movement.walk_speed);

        match movement.movement {
            PlayerMovement::NoMovement => {}
            PlayerMovement::AWSD => {
                action_state.set_axis_pair(&PlayerActions::Direction, Vec2::ZERO);
            }
            PlayerMovement::Direction(vec2) => {
                action_state.set_axis_pair(&PlayerActions::Direction, vec2);
            }
            PlayerMovement::Target(vec2) => {}
        }

        // action_state.set_value(&PlayerActions::MoveSpeed, movement.walk_speed);
    }
}
pine cape
#

I don't think it's related to player speed. Basically you're seeing a rollback?
I would first try to understand what's causing a rollback, and if the client and server are seeing the same inputs at the same ticks.
This is not using HostClient mode, right?

Maybe try enabling debug logs for lightyear_prediction::rollback and add some logs similar to what I have here:
https://github.com/cBournhonesque/lightyear/blob/main/examples/avian_3d_character/src/shared.rs#L187

GitHub

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

peak ice
#

Ok! I’ll take a look into it!

#

Thank you:)

pine cape
#

Normally you shouldn't have any rollback if other players are not involved

inner hill
#

got it working :D, didn't realize i needed my receiver to be in Update not PreUpdate:

2025-11-10T02:26:31.886671Z  INFO arch_core::voxel::commands: Received message: SetVoxelsSdf { origin: IVec3(763, 45, 11), sdf: Sphere(Sphere { radius: 5.0 }), voxel: Air, params: SetVoxelsSdfParams { within: 0.0, can_replace: VoxelSet(65529) } }
pine cape
#

Your receiver can be in PreUpdate, after MessageSystems::Receive

inner hill
#

Is there a marker component for something like With<LocalClient>? I see HostClient, but I'm not seeing anything special on my client side. I added my own LocalClient component and do Or<(With<HostClient>, With<LocalClient>)> for now

pine cape
#

On the client you should just have a single Client entity

pulsar stream
unkempt sedge
unkempt sedge
#

@pine cape Is there a slight change that a input may never be received due to packet loss and so on, example: A system like this to never receive it is input message

fn remove_walking(trigger: On<Complete<MoveInput>>, mut commands: Commands) {
    let player = trigger.event().event_target();
    commands.entity(player).remove::<Walking>();
}```
vagrant moth
#

Hello all 🙂

Does anyone of you have mixed : https://github.com/idanarye/bevy-tnua with lightyear ?

For now I'm using leafwing-input that are "nativelly supported" by lightyear and it works fine.

But as Tnua already have plenty of logic that I wouldn't like to redo I was wondering how easy it's to integrate with lightyear.

pine cape
unkempt sedge
inner hill
stray sinew
#

A follow up on the client log spam on non-visible despawn, it appears to happen when a non-visible child entity is despawned.

pine cape
pine cape
inner hill
#

is it set up by default for messages somehow? If not no, the last portion lagging a bit is because I have a separate limiter on remeshing stuffs

pine cape
stray sinew
#

Here is a video clearly displaying odd input snap behaviour with logs: https://streamable.com/d366vq. In the video I ran the exact same executable twice, the first run worked flawlessly, the second run had buggy input behaviour without any apparent rollbacks or corrections.

Relevant Info:

  • TPS is set to 5

  • The ship's rotation is determined by Rotation(Quat) which is predicted and correctly registered with FrameInterpolation.

  • During non-buggy runs, my client logging system appears to print twice in the same tick despite only 1 player being present.

  • During buggy runs, my server logging system spams: "bevy_enhanced_input::action::fns: action protocol::player_action::MovePlayer (213v0) expects Axis3D, but got Bool"

  • Move action is declared like:

#[derive(Debug, InputAction)]
#[action_output(Vec3)]
pub struct MovePlayer;

And spawned like:

commands.spawn((
    ActionOf::<PlayerContext>::new(player),
    Action::<MovePlayer>::new(),
    bindings![
        (KeyCode::KeyW, SwizzleAxis::YXZ),                // +Y
        (KeyCode::KeyS, Negate::all(), SwizzleAxis::YXZ), // -Y
        (KeyCode::KeyA, Negate::all()),                   // -X
        KeyCode::KeyD,                                    // +X
        (KeyCode::KeyQ, Negate::all(), SwizzleAxis::YZX), // -Z
        (KeyCode::KeyE, SwizzleAxis::YZX),                // +Z
    ],
));
  • The attached image is my Prediction and Input config. I have tried tweaking the config in an attempt to fix but have not succeeded.
pine cape
# inner hill is it set up by default for messages somehow? If not no, the last portion laggin...

No it's not setup by default. It's applied per Link (on the sending side) for example like this: https://github.com/cBournhonesque/lightyear/blob/main/examples/priority/src/server.rs#L60
You can send a message with a priority value to control which messages should be prioritized

GitHub

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

pine cape
#

During buggy runs, my server logging system spams: "bevy_enhanced_input::action::fns: action protocol::player_action::MovePlayer (213v0) expects Axis3D, but got Bool"

hm i'm wondering if some input mapping failed and the inputs are being sent for the wrong entity

stray sinew
#

Could It be because I have multiple entities with ControlledBy set to my client entity? I'll try removing those and retest.

pine cape
#

not sure; do you have an easy repro?

stray sinew
stray sinew
#

Axis2D and Axis3D seem to be causing the issues, my boolean inputs don't seem to have any jitter or snap problems if that helps

pine cape
#

In their case the got Bool log appears only once at the beginning, which is expected.
But you have it constantly, which means that the input is somehow wrong.

unkempt sedge
unique plover
north skiff
#

For actions that a player can make, that should only be fired once, and I don't want to accidentally trigger two frames in a row, how should they be handled? Should the shared system that consumes the action just reset the ActionState? (This is without BEI or leafwing.)

pine cape
pine cape
vagrant moth
# unique plover Yes, we have integrated tnua with Lightyear in our game. We used to have Leafwin...

Oh nice ! 😄
Do you have some links to documentation or examples ?

Do you have pros and cons for leafwing vs BEI ?

From what i'm seeing here, I could use whatever input I want as long as I use tnua basis... https://github.com/idanarye/bevy-tnua/blob/main/examples/example.rs

Also I guess TnuaController is only client side and TnuaAvian3dSensorShape is both client server, and then same logic etc..etc.. for normal movement with lightyear ?

(sorry for the tons of questions, thanks for your help ! 🙂 )

unkempt sedge
north skiff
#

If a client joins a server, the Transform of other players still seems to be at the origin, as long as they don't move. How could this be? Is it something with my Player component requiring a Transform?

unique plover
unique plover
# vagrant moth Oh nice ! 😄 Do you have some links to documentation or examples ? Do you hav...

Unfortunately I don't have a public example, but since I've ran into some edge cases with HostClient I'll probably be setting up a Lightyear example that matches my architecture.

Tnua is expecting a more "pull style" architecture since it's feeding everything into the basis at once, so in that sense it's easier to integrate Leafwing if all you care about is the player controller. That said, BEI is better for literally everything else because of its flexibility and "push style" architecture with observers. Unfortunately I ran into inconsistency with pull-style in BEI even though it's technically supported. It looks like BEI is a major contender for upstreaming, so I bit the bullet and made the switch. What I had to do to feed the basis was to create an "input accumulation" component, populate that with observers and clear it after reading it in the controller system.

pine cape
pine cape
north skiff
north skiff
#

(Currently I'm just syncing Transform directly.)

pine cape
#

Transform is probably being added by an observer or required components

craggy veldt
#

How can i sync timers? When i replicate a timer it starts new. I thought about just using time and do the logic itself. Is there a time resource that is synced between server and client?

unkempt sedge
#

But replicating a timer should just work in my opinion if it is not something might be wrong

#

2025-11-13T12:07:56.710014Z INFO lightyear_prediction::despawn: inserting prediction disable marker self.entity=758v1 @pine cape Mind if I downgrade this log he is quite spawmy (occurs everytime i call prediction_despawn )

north skiff
#

I understand an observer doing it.

#

A required component shouldn't override a component if already present.

north skiff
#

And a component already being present (due to required component inserting it) should not prevent replication from updating it.

pine cape
north skiff
unkempt sedge
#

@pine cape Bevy metrics dashboard is causing the crate to compile 0.16.1, which in turn is causing the ci to fail on docs

#

Mind if i remove it :?

unkempt sedge
#

@pine cape So my game has a teleportation logic, where you kinda teleport player when he changes levels. Since he is predicted he starts to rollbacks to previous states he was in whenever I teleport any ideas on how to avoid this nuisance, is there any componnet that just disables prediction

pure drum
#

Q: what's the best way to avoid host-client setup (as it gives me too many inconsistencies/edge cases)? run two separate bevy apps in different threads/custom loop? crossbeam to link like the tests use?

stray sinew
pure drum
pine cape
pine cape
pine cape
pure drum
pine cape
pine cape
jade ember
#

hello everyone, encountering a bit of a weird thing, any help would be appreciated

i'm setting up a project right now using lightyear and hostclient mode.
i have two buttons, one for hosting and one for joining.

when you click hosting, it spawns a server and a client, starting the server and connecting the client.
then in a second instance of the game i click on join and it spawns a client that connects to the server.

this works.

then when i disconnect, i despawn the server and/or client after disconnecting client and stopping server.
when i then click on host again (spawning another server and client), it seems to remember players/connections from before (?)

am i misunderstanding something, doing something wrong, or missing something? what's the intended way on doing this?

jade ember
unkempt sedge
#

@pine cape okay them will make a quick pr, btw I was meaning to talk to you. Why the excessive craterization of lightyear (importing bevy ecs and so on), I doubt that diminishes compile times. And it seens to increase the amount of code in the crate by tenfold

pine cape
pine cape
jade ember
# pine cape So the difference is that you despawn the host-client instead of simply disconne...

no. i was a bit unclear, i disconnected before already.

the difference is that i now spawn a server at startup and keep that one spawned no matter if you host or join.
the server then just starts when you want to host and stops when you disconnect.
when starting the server i also spawn the hostclient and connect to the server.
when i stop the server, the client disconnects and then despawns.

when i join, the server stays stopped and i just spawn a new client. and when disconnecting i disconnect the client and then despawn the client.

#

so despawning and spawning the server again seemed to have caused the issue. idk if this was intended.

i hoped i could just have a server when needed and don't have one when just joining

unkempt sedge
unique plover
# jade ember so despawning and spawning the server again seemed to have caused the issue. idk...

Are these your own player entities that are hanging around, or the Lightyear managed client entities? If it's the former, we use a combination of Lifetime::SessionBased and DespawnOnExit with our own ServerStates

let player = commands.spawn((
    PlayerComponent,
    DespawnOnExit(ServerStates::Listening),
    ControlledBy {
        owner: client,
        lifetime: Lifetime::SessionBased,
    },
    Replicate::to_clients(NetworkTarget::All),
));

You could probably add a DespawnOnExit to the Lightyear client entities if they're the issue.

pine cape
jade ember
# unique plover Are these your own player entities that are hanging around, or the Lightyear man...

Its the player entities, but i despawn everything on disconnect.

but for some reason, these observers just trigger one more time any time i connect again

pub(crate) fn handle_new_client(trigger: On<Add, LinkOf>, mut commands: Commands) {
    info!("New client connected: {:?}", trigger.entity);

    commands
        .entity(trigger.entity)
        .insert(ReplicationSender::new(
            SEND_INTERVAL,
            SendUpdatesMode::SinceLastAck,
            false,
        ));
}

pub(crate) fn spawn_player(
    trigger: On<Add, Connected>,
    query: Query<&RemoteId, With<ClientOf>>,
    mut commands: Commands,
) {
    let Ok(client_id) = query.get(trigger.entity) else {
        warn!("Connected client has no RemoteId; cannot spawn player");
        return;
    };
    let client_id = client_id.0;
    info!("Spawning player with id: {}", client_id);
    commands.spawn((
        Replicate::to_clients(NetworkTarget::All),
        PredictionTarget::to_clients(NetworkTarget::Single(client_id)),
        InterpolationTarget::to_clients(NetworkTarget::AllExceptSingle(client_id)),
        ControlledBy {
            owner: trigger.entity,
            lifetime: Default::default(),
        },
        PlayerId(client_id),
        PlayerMarker,
        Name::new(format!("Player {}", client_id)),
        DespawnOnExit(IsConnected),
    ));
}
#
  1. at start
  2. first connect
  3. first disconnect
  4. second connect
#

and when i start a second instance of the game that just joins (only client, no server) it immediately gets 2 Netcode players

unique plover
#

Additionally, maybe your IsConnected may not be updated properly? This is what ours looks like:

fn server_stopped_observer(
    _trigger: On<Add, Stopped>,
    mut next_server_state: ResMut<NextState<ServerStates>>,
) {
    next_server_state.set(ServerStates::Disconnected);
}
pine cape
unkempt sedge
#

@pine cape Is RemoteEntityMap not available to use? On 0.25.3 I can't export it. I need it because I want to check if an entity is already mapped on the client. Note: even though the entity has the Replicate component and is already added to its room on the server, if I send a message right after that, it fails to map that entity as it is yet to be mapped on the client

pine cape
#

Yes if the message is received before the client spawns the entity, the mapping will fail

pine cape
#

@unkempt sedge i released a new version

unkempt sedge
#

@pine cape Okey thanks btw, I made a PR to eliminate metrics the visualizer features seens to be unused can i remove it?

#

Docs compilke now

pine cape
karmic sky
#

Hi @pine cape
I think I have found an issue related to network visibility.

When many entities go from visible to not visible for a given client, it takes quite a lot of time before the server eventually stops sending "actions" to the client. (When I say "actions", I'm referring to "Recv Actions" from the DebugUIPlugin, so spawn/despawn messages).

I don't really know what those actions are though, maybe despawn actions are being queued multiple times...

I managed to reproduce the issue in the network_visibility example. To make it more obvious, I added more CircleMarkers only on the left part of the area and spawned the player on the right side (far from any circles).

When the player spawns, if I don't move the player only receive pings, no "updates", no "actions". That's the expected behavior.

When I move to the left part, all circles appear. Then I go back to the right side and all circles disappear, BUT I continue to receive many "actions" for multiple seconds.

Here is a video that demonstrates the issue: https://streamable.com/7rzdac

(I can create a github issue if needed)

ps: it's not really noticeable with just a few entities like the 4 circle markers in the initial example, here is how I have updated the init fn to spawn more:

pub(crate) fn init(mut commands: Commands) {
    // spawn dots in a grid
    let start = -30;
    let end = 30;
    for x in start..0 {
        for y in start..end {
            commands.spawn((
                Position(Vec2::new(
                    x as f32 * GRID_SIZE / 8.,
                    y as f32 * GRID_SIZE / 8.,
                )),
                CircleMarker,
                Replicate::to_clients(NetworkTarget::All),
                // Use network visibility for interest management
                NetworkVisibility::default(),
            ));
        }
    }
}

I spawned the player at: Position(Vec2::new(150., 0.)) and also updated its movement speed to 20 in shared_movement_behaviour

Watch "Recording 2025-11-16 124114" on Streamable.

▶ Play video
jade ember
# unique plover Additionally, maybe your `IsConnected` may not be updated properly? This is what...

don't think thats an issue.

i update the connection state here:

fn spawn_server(mut commands: Commands, networking_mode: Res<NetworkMode>) {
    let _server = commands
        .spawn((spawn_host(&networking_mode),))
        .observe(
            |trigger: On<Add, Started>,
             mut connection_state: ResMut<NextState<ConnectionState>>,
             mut app_state: ResMut<NextState<AppState>>| {
                info!("Server started, entity: {:?}", trigger.entity);
                connection_state.set(ConnectionState::Connected { is_host: true });
                app_state.set(AppState::Lobby);
            },
        )
        .observe(
            |trigger: On<Add, Stopped>,
             mut connection_state: ResMut<NextState<ConnectionState>>,
             mut app_state: ResMut<NextState<AppState>>| {
                info!("Server stopped, entity: {:?}", trigger.entity);
                connection_state.set(ConnectionState::Disconnected);
                app_state.set(AppState::MainMenu);
            },
        )
        .id();
}

and IsConnected is a ComputedState:

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct IsConnected;

impl ComputedStates for IsConnected {
    type SourceStates = ConnectionState;

    fn compute(sources: Self::SourceStates) -> Option<Self> {
        match sources {
            ConnectionState::Connected { .. } => Some(IsConnected),
            _ => None,
        }
    }
}
pine cape
jade ember
#

@pine cape how can i insert a registered component on the client and have it replicated to all the others?

on the book in the Page Protocol i can see i can do:

app.register_component::<PlayerId>(ChannelDirection::ServerToClient)
  .add_prediction::<PlayerId>(ComponentSyncMode::Once)
  .add_interpolation::<PlayerId>(ComponentSyncMode::Once);

but i think this is outdated, as i can't find the ChannelDirection

jade ember
pine cape
jade ember
#

hm, its still not working

the client now has a ReplicationSender, but when i insert a component into my replicated Entity client side, the server still isnt receiving it

pine cape
#

and the entity has Replicate? and the client-of has ReplicationReceiver?

jade ember
#

oh, does it need Replicate on client side too? just checked and currently it only has it on server side

pine cape
#

well yes if you replicate from client to server

jade ember
#

i guess that makes sense, thats what i missed

#

my bad haha

jade ember
#

nope, still not working, unless i'm still dumb and miss something (sorry if thats the case lol)

first image is hostclient view of the replicated entity (named Netcode(0)) and the ClientOf
on the server the replicated entity has Replicate
and the ClientOf has ReplicationReceiver and ReplicationSender

second image is clientview of the replicated entity. it has a Replicate and also has the PlayerIsReady Component which I insert on the client and i'm trying to have replicated back to the server (its registered and all)

third image is clientview of the client (it was too big to put it together with the second image), it got ReplicationReceiver and ReplicationSender

am i missing anything?

pine cape
#

you're trying to replicate an entity from client 1 to server, and then from server to other clients?

jade ember
#

the server does the initial spawn and replicates the entity to all connected clients

#

i then try to insert a single component from the individual clients and want that one to be replicated to the server so he can replicate it back to all clients

pine cape
#

ah; i don't think that's possible

#

or rather, what you could do is:

  • spawn the entity on the server and replicate to all clients
  • transfer authority from server to client 1, now client 1 is responsible for simulating the entity.
  • add component on client 1, which will get replicated to the server (which will replicate to other clients)
jade ember
#

hm, or would it be easier to work with a message?

the client sends a message to the server telling he's ready, and the server inserts the component?

pine cape
#

You could do that. But ultimately who do you want to have authority on the entity?

jade ember
#

probably that the host has authority on everything

#

so thats probably how i'll do it

#

thank you :)

pine cape
#

by host, you mean the server? or the client?
Because if it's the client, then you probably want authority transfer?

jade ember
#

well, host is server and client as i do hostclient lol

unique plover
inner hill
#

hmm... is there a way to do client p2p partially while using steam stuffs

#

Need to look into it more but it'd be nice if I could send voxel chunk updates from the authoritative client for that area and the server/host client is mainly arbitrating authority

#

not the biggest deal I think, but I think it just ups the upload bandwidth requirement for the host/server

pine cape
#

you mean send from client 1 to client 2 directly?

#

it's not really something I explored too much. Theoretically you could as long as you create a Link between the 2

inner hill
#

hmm

#

I'll explore it more after I get the version just sending to the server working

inner hill
sly frigate
#

I encountered an issue when building a project with Lightyear.
When I start the server, movement between PC client and PC client works fine with no issues. However, with the WASM web client, when I press the movement keys, there's a very long delay before the server and the other PC client receive the message and show the movement.
client:

fn startup(
    mut commands: Commands, 
    mirlegend_settings:Res<MirlegendSettings>,
    mut next_screen_state: ResMut<NextState<Screen>>,
) -> Result {

    let auth = Authentication::Manual {
        server_addr: SocketAddr::new(IpAddr::V4(mirlegend_settings.shared.server_addr),mirlegend_settings.shared.server_port),
        client_id: rand::random(),
        private_key: Key::from(mirlegend_settings.shared.private_key),  
        protocol_id: mirlegend_settings.shared.protocol_id,              
    };
    let netcode_config = NetcodeConfig {
        token_expire_secs: 300,  
        ..Default::default()
    };
    let certificate_digest = include_str!("../../../../certificates/digest.txt").to_string();
    let conditioner = LinkConditionerConfig::average_condition();
    let client = commands
        .spawn((
            Client::default(),
            LocalAddr(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0,0,0,0)), 0)),
            PeerAddr(SocketAddr::new(IpAddr::V4(mirlegend_settings.shared.server_addr),mirlegend_settings.shared.server_port)),
            Link::new(Some(RecvLinkConditioner::new(conditioner.clone())),),
            ReplicationReceiver::default(),
            PredictionManager::default(),
            NetcodeClient::new(auth, netcode_config)?,
            WebTransportClientIo { certificate_digest },
            Name::from("Client"),
        ))
        .id();
    commands.trigger(Connect {
        entity: client,
    });    
    next_screen_state.set(Screen::InConnect);
    Ok(())
}```
#

server:

use core::net::{IpAddr, SocketAddr};
use mirofme_common::settings::MirlegendSettings;
use lightyear::prelude::server::*;
use lightyear::prelude::*;

use bevy::tasks::IoTaskPool;
use async_compat::Compat;

pub(super) struct ServerConnectBasePlugin;

impl Plugin for ServerConnectBasePlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, startup);        
    }

}

/// Start the server
fn startup(mut commands: Commands, mirlegend_settings:Res<MirlegendSettings>) -> Result {
    let netcode_config = NetcodeConfig {
        protocol_id: mirlegend_settings.shared.protocol_id,
        private_key: mirlegend_settings.shared.private_key,
        ..Default::default()
    };
    let identity = IoTaskPool::get()
        .scope(|s| {
            s.spawn(Compat::new(async {
                Identity::load_pemfiles(("certificates/cert.pem").to_string(), ("certificates/key.pem").to_string())
                    .await
                    .unwrap()
            }));
        })
        .pop()
        .unwrap();

    let server = commands
        .spawn((
            NetcodeServer::new(netcode_config),
            LocalAddr(SocketAddr::new(IpAddr::V4(mirlegend_settings.shared.server_addr),mirlegend_settings.shared.server_port)),
            WebTransportServerIo {
                certificate: identity,
            },
            Name::from("Server")
        ))
        .id();
    commands.trigger(Start {
        entity: server,
    });

    // 启动相机以及实体检测器
    commands.spawn(Camera2d);
    Ok(())
}```
unkempt sedge
#

@pine cape Opened 4 quickie qol prs, I think a friend of mine will open a new one fixing the ci completely. When he does so would you kindly release again? I really want that entitymap e.e

pine cape
#

You can import the entity mapping directly from lightyear_serde also

unkempt sedge
#

@pine cape if you dont slash, lightyear into portions I cannot (lightyear replication and so on) you cant.

pine cape
sly frigate
# pine cape do you also get this with the examples?

I can run the official simple_box example smoothly. It might be that I don't have a deep enough understanding of Lightyear - I thought it was an issue with my network connection code, but I found that it wasn't. I'll carefully look for issues in other areas myself

north skiff
#

I've noticed some oddities with WebTransport on web too, but I haven't dug into it yet. Just called it "good enough" and moved on for now.

unkempt sedge
#

2025-11-18T13:33:03.753588Z WARN bevy_enhanced_input::action::fns: action MoveInput (607v0) expects Axis2D, but got Bool`` @pine cape Ahmm do you know what this is about? I have no idea on what causes this warning just wondering what system I should look at

pine cape
#

It's because of how BEI integration is setup, unless it's spammed all the time you can just ignore it

unkempt sedge
#

i mean it spawned for a while but not continously i dont see any rollbacks and so on

pine cape
#

You can just ignore it then

#

I disabled that log in the examples

dry flower
#

@pine cape thanks for merging my PR so quickly 🙂 going to have a few more on the way making timline syncing more robust overall. I agree doing this in PostUpdate isn't ideal, and I also agree with the comment (about sync_timelines) that says we could run this right after RunFixedMainLoop - applies to sync_from_local_timeline as well. But I wanted to keep this PR scoped to just the bug fix

pine cape
#

Really appreciated, as the whole sync logic could use more eyes!

dry flower
#

Yeah, I think something like a kalman filter could make the RemoteTimeline recover much faster from server side lag spikes and stuff like that

#

just gotta find the right balance between bulletproofness and simplicity. obviously this isn't a fighter jet, we don't need to be spending more than, like, 100 cycles on clock sync

karmic sky
pine cape
#

Yes!

pine cape
unkempt sedge
#

@pine cape would it be fine to add a simple typos CI check?

unkempt sedge
#

@pine cape Also curiosity, I know lightyear does position to transform but never actually got why, it does not seem to be a source of indeterminism. Would you mind telling me the reasons why? Comments seem little disperse, btw I ask because I would like to avoid systems like this

fn receive_server_teleports(
    mut message_reader: Single<&mut MessageReceiver<TeleportPredicted>>,
    position: Query<Has<Position>>,
    mut commands: Commands,
) {
    for message in message_reader.receive() {
        if message.target == Entity::PLACEHOLDER {
            info!(
                "Player on boot up is teleported on server, but the entity does not exist on client. Note - TODO lightyear only send message if mapped"
            );
            return;
        }
        debug!("Teleported {} to {}", message.target, message.destination);

        let has_position = position.get(message.target).unwrap_or_default();
        // Due to lightyears weird position to transform necessity
        if has_position {
            commands
                .entity(message.target)
                .insert(Position(message.destination));
        } else {
            Transform::from_translation(message.destination);
        }
    }
}```
silent patrol
#

upd. disregard, I might have found a bug in my manual id mapping logic

I'm not sure how this happens, but some replicated entities randomly become children of other entities... absolutely no idea why though, as those entities aren't related in any way. My only guess is that lightyear mistakenly assigns some entities as children of others

slow kernel
jade ember
#

hello!
I struggle to get bevy_enhanced_input to work with lightyear. i tried following the example and got it somewhat working.

when my client connects to the server (hostclient) i sometimes get this error:

2025-11-19T20:10:15.055318Z ERROR lightyear_inputs::client: received input message for unrecognized entity entity=PLACEHOLDER target_data.states=BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [] } end_tick=Tick(603)

sometimes i get a lot of them, other times just once or twice, but only at the beginning. this is concern 1

concern 2, currently the client that joins gets for both clients (client and hostclient) the inputs replicated, but the hostclient only gets his own input replicated. and i don't know if it is a problem or not, and how to fix it.

concern 3 is that my movement code:

fn movement_host(
    trigger: On<Fire<Movement>>,
    mut position_query: Query<&mut Position, Without<Predicted>>,
) {
    println!("movement_host1");
    if let Ok(position) = position_query.get_mut(trigger.context) {
        println!("movement_host2");
        shared_movement_behaviour(position, trigger.value);
    }
}

only gets triggered on the hostclient movement itself, but not when the client moves. so currently the client only moves on his end and then gets moved back to where the server thinks he is.

i guess second and third concern have todo with each other.

any idea what i might have missed?

pine cape
silent patrol
pine cape
pine cape
jade ember
unique plover
jade ember
unkempt sedge
pine cape
pine cape
unkempt sedge
#

Guess I will try to see the why now tho

pine cape
#

maybe the system order for AvianMode::Transform is not correct; i haven't tested it in a rollback scenario

stray sinew
craggy veldt
jade ember
unique plover
#

I have both server and client features enabled and hostclient inputs are replicated, although they seem to be more unreliable than client rebroadcasted inputs which is something I've been debugging myself.

pine cape
pallid vector
#

I'm currently debugging why only the parent entity is replicating while setting up networking for my project, and I just wanted to flag that the docs for DisableReplicateHierarchy seem outdated. It talks about both disabling and enabling replication, and it refers to a component ReplicateLikeParent that doesn't exist. Based on the name my guess is that most of the second paragraph is wrong?

pine cape
#

ReplicateLikeParent is now ReplicateLike

pallid vector
pine cape
#

Yep that's incorrect, I'll update

random palm
#

Hi i'm trying to implement a fps camera using mouse motion with bevy enhanced input but when i add Input delay, player camera moves after the delay. Is this normal behaviour ? Do i need to remove input delay ?

unique plover
pine cape
#

I think that works, I could also add a component that marks an input as Local, which would remove input delay + not replicate.
Or do you need the player's orientation to be replicated to the server?

lyric mauve
#

hey guys! i was experimenting with WebTransport and saw that some of the lightyear devs raised this issue about disconnects when switching tabs: https://github.com/w3c/webtransport/issues/600

i am facing the exact same issue with bevy_renet2, and was curious how you guys solved this (if at all)?

GitHub

Something that has us with our heads spinning is why webtransport closes connections currently in Chrome when the user switches tabs. This is contrary to web sockets, which are scoped to the browse...

pine cape
random palm
#

When i connect with other client, first client transform not synced up unless first client moves

random palm
#

@unique plover i need help about bevy_tnua integration

#

do you use

app.add_plugins(lightyear::avian3d::plugin::LightyearAvianPlugin {
            replication_mode: AvianReplicationMode::Transform,
            ..default()
        });
        app.add_plugins(
            PhysicsPlugins::default()
                .build()
                // disable syncing position<>transform as it is handled by lightyear_avian
                .disable::<PhysicsTransformPlugin>()
                .disable::<IslandSleepingPlugin>()
                .disable::<PhysicsInterpolationPlugin>(),
        );

This ?, somehow my positions get desynched

unique plover
jade ember
unique plover
#

Both server and client plugins are present on all clients though.

jade ember
#

but i'll look into it again, maybe i made a mistake back then

update: yes, have made a mistake back then -_-

#

anyways, i would have expected it to work the way i've done it. or is this to be expected?

#

i'll open an issue for this otherwise

pine cape
unkempt sedge
#

2025-11-25T18:37:40.042449Z WARN bevy_ecs::error::handler: Encountered an error in command <bevy_ecs::system::commands::entity_command::remove<psycho_player::shared::state_machine::Interacting>::{{closure}} as bevy_ecs::error::command_handling::CommandWithEntity<core::result::Result<(), bevy_ecs::world::error::EntityMutableFetchError>>>::with_entity::{{closure}}: The entity with ID 505v64 does not exist (enable track_location` feature for more details)

If you were attempting to apply a command to this entity,
and want to handle this error gracefully, consider using `EntityCommands::queue_handled` or `queue_silenced`.`

@pine cape Just curious have you encountered a warn such as this one, it occurs everytime I disconnect my client and it is entities get despawned with him

#

@pine cape also you are in our ui

jade ember
unkempt sedge
pine cape
#

You can use try_despawn instead of despawn

#

@jade ember does my latest PR fix your issue?

jade ember
#

i changed it now anyways so that i only spawn the server when needed, so it's not an issue currently for me

random palm
#

How can i log how many kb/s transmitted or received by both client and server ?

pine cape
random palm
pine cape
#

Try enabling the metrics feature

cloud patio
#

how would i build a chat system into lightyear? doesn't look like InputAction can support strings

pine cape
#

Chat shouldn't be an input as it doesn't need to be tick-synced. You can just use create a custom Message to store your string and send it to other peers

jade ember
#

is there any example where i can see how i can configure lightyear and avian to work with transform instead of position?

pine cape
#

It should just be using AvianReplicationMode::Transform

jade ember
# pine cape It should just be using AvianReplicationMode::Transform

the client that joins crashes now as soon as the player lands on the ground and i get this error:

thread 'main' panicked at C:\Users\zwaze\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\avian3d-0.4.1\src\dynamics\solver\islands\mod.rs:522:9:
assertion failed: contact.island.is_none()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `avian3d::collision::narrow_phase::update_narrow_phase<avian3d::collision::collider::parry::Collider, ()>`!
Encountered a panic in system `avian3d::schedule::run_physics_schedule`!
Encountered a panic in system `bevy_app::main_schedule::FixedMain::run_fixed_main`!
Encountered a panic in system `bevy_time::fixed::run_fixed_main_schedule`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
#

which is why i was originally asking, maybe i'm missing any replication registrations and so on, but in the test i don't see really anything that differs from what i have

pine cape
#

You need to disable the island plugin, check the examples

#

Plus some other plugins

jade ember
jade ember
#

because in the avian 3d characters example it is commented out and not disabled

pine cape
#

It causes issues with rollbacks, the avian 3d characters probably works because there is no rollback? Idk

jade ember
#

its also not disabled in the avian_physics and the fps examples lol

#

but thanks, i'll try that out

jade ember
#

@pine cape should/can i still do this when switching to AvianReplicationMode::Transform?

app.register_component::<LinearVelocity>()
    .add_prediction()
    .add_should_rollback(linear_velocity_should_rollback);

app.register_component::<AngularVelocity>()
    .add_prediction()
    .add_should_rollback(angular_velocity_should_rollback);
pine cape
#

Probably yes, otherwise you will be mispredicting on the client

jade ember
# pine cape You need to disable the island plugin, check the examples

i disabled the IslandPlugin but now i get this error

thread 'Compute Task Pool (8)' panicked at C:\Users\zwaze\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\avian3d-0.4.1\src\dynamics\solver\plugin.rs:398:51:
index out of bounds: the len is 1 but the index is 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `avian3d::dynamics::solver::plugin::prepare_contact_constraints`!
Encountered a panic in system `avian3d::schedule::run_physics_schedule`!
Encountered a panic in system `bevy_app::main_schedule::FixedMain::run_fixed_main`!
Encountered a panic in system `lightyear_prediction::rollback::run_rollback`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
jade ember
#

hm, it only crashes once i do something, any kind of input...

nvmd, disabled all the movement systems and it still crashes

random palm
#

How we can transfer authority right now ?

#

and also what is this error means:

2025-11-28T12:09:28.425177Z ERROR lightyear_replication::send::plugin: Error buffering ActionsMessage: Serialization(InvalidValue)

I'm trying to spawn 9 server controlled entities with prediction

commands.spawn((
            ZombieBundle::new(variant, position),
            Replicate::to_clients(NetworkTarget::All),
            InterpolationTarget::to_clients(NetworkTarget::All),
            PredictionTarget::to_clients(NetworkTarget::All)
        ));
#

and also is there a way to not rollback rotation ? make it only client authoritive ?

jade ember
pine cape
pine cape
pine cape
#

@pure drum @unique plover the issue with the action `EvaMoveAction` (`427v0`) expects `Axis2D`, but got `Bool`
errors seems to be that the client is started a bit later than the server, but no more than 10 ticks before.
Because of that the hard tick sync does not kick in (I only snap the client tick to the correct tick if it is more than 10 ticks away from the ideal tick). Instead the ticks are adjusted by changing the speed of the client timeline, but it takes time to adjust

#

I can push a fix to always do a hard tick sync the first time we are trying to sync

random palm
pine cape
pure drum
# pine cape This should fix the input sync issues you've been observing: https://github.com/...

Interesting.. pointing to main now and it looks like the client is in sync (if im reading it correctly), but i still get the Axis2D vs Bool warning. On my setup is happens 100% of the time now.

2025-11-29T19:25:17.053510Z TRACE lightyear_inputs::server: received input message tick=Tick(16) client_id=RemoteId(Netcode(13633984869232431464)) action=EvaContext message.end_tick=Tick(16) message.inputs=[PerTargetData { target: Entity(425v0), states: BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } }]
2025-11-29T19:25:17.053520Z DEBUG lightyear_inputs::server: Rebroadcast input message InputMessage { interpolation_delay: None, end_tick: Tick(16), inputs: [PerTargetData { target: Entity(425v0), states: BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] } }], rebroadcast: false } from client RemoteId(Netcode(13633984869232431464)) with rebroadcaster None action=BEIStateSequence<EvaContext>
2025-11-29T19:25:17.053530Z TRACE lightyear_inputs::server: Updating InputBuffer: InputBuffer<DebugName { name: "lightyear_inputs_bei::input_message::ActionsSnapshot" }>:
 Tick(16): ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }
 using: BEIStateSequence { start_state: ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }, diffs: [SameAsPrecedent, SameAsPrecedent, SameAsPrecedent, SameAsPrecedent] }
2025-11-29T19:25:17.053550Z TRACE lightyear_inputs::input_message: input buffer after update: InputBuffer { start_tick: Some(Tick(16)), buffer: [Input(ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) })], last_remote_tick: Some(Tick(16)) }
2025-11-29T19:25:17.053866Z TRACE lightyear_inputs::server: input buffer on server tick=Tick(17) server=419v0 input_buffer=Mut(InputBuffer { start_tick: Some(Tick(16)), buffer: [Input(ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) })], last_remote_tick: Some(Tick(16)) })
2025-11-29T19:25:17.053895Z  WARN bevy_enhanced_input::action::fns: action `EvaMoveAction` (`425v0`) expects `Axis2D`, but got `Bool`
pine cape
#

Hm the client is still behind the server...

pure drum
#

Oh 😐

#

(just making 100% sure im actually using the latest main)

#

yup looks like it

#

if you need to look at my code, i pushed the dep change to main

pine cape
compact cypress
#

I'm trying to create a Host in separate mode (2 bevy apps I think, since multi-world is not a thing). Couldn't find any examples on that. Any ideas?

#

Someone mentioned crossbeam I believe

pine cape
#

But yeah the idea is to create two apps connected by crossbeam

random palm
#

How can i get same elapsed_time for server and client ?

#

or something similar to that

#

trying to do this:

 let current_tick = timeline.now().tick();
    if current_time - weapon.last_shot_time < stats.fire_rate {
        info!("Fire rate not met");
        return Ok(());
    }
random palm
#

I figured

pine cape
#

Yep I would base it on ticks

stray sinew
#

Would it be possible to make NetworkVisibility::is_visible public? Having this exposed would be useful for implementing a rate-limited visibility gain/lose system.

pine cape
#

Sure, can you PR it?

karmic sky
#

Hi, I guess my issue is related to this issue https://github.com/cBournhonesque/lightyear/issues/1336 but when working with only interpolation and so, no input delay, many inputs are never processed by the server (because they are from past I guess), adding input delay fix the issue but.. add delay ^^
Using only native inputs btw if that can help

pine cape
#

and you have no artificial lag added?

#

things are known to not work properly with 0 artifical lag added

karmic sky
#

I have the issue with or without LinkConditionerConfig

I managed to replicate the issue by updating the network_visibility example, don't have it anymore.. but tldr is only use interpolation InterpolationTarget::to_clients(NetworkTarget::All) and no PredictionTarget for the player, remove the movement system from the client (only update player from server) run the example and try to move, you will see that player does not move or never stop moving when it start to move

I did my test with UDP transport

karmic sky
#

Here is a video, not ez to show... https://streamable.com/uce5o1
This is using this version of the example: https://github.com/OlivierCoue/lightyear/commit/927bbdcfa4931683c3a9d38195f0f561ac9695c7
And you'r right that with more ping/lag its less common, but here I use:

let conditioner = LinkConditionerConfig {
    incoming_latency: Duration::from_millis(20),
    incoming_jitter: Duration::from_millis(5),
    incoming_loss: 0.00,
};

Which is standard latency with fiber/close server (I believe so) and it appears a lot

pine cape
#

It's possible that the sync logic changes have introduced some bugs, i will take a look

stray sinew
pine cape
#

I'm thinking of changing some of the replication internals.
Things i'm unhappy with:

  • the current design allows for a lot of flexibility since any entity can be a Link, and you could have multiple Servers in the same app. However this adds a lot of complexity for not much payoff (i don't think anyone is using multiple servers in the same app anytime soon)
  • even if multiple-servers were a thing, what we would want is multiple "server types": i.e. we could have a SteamServer + UdpServer + WebTransportServer in the same app, each tracking their own sets of clients, but the App as a whole runs with the role of a server, with a single global timeline
  • some things are penalized because of this flexibility. For example even if you run lightyear as a client with lient to server replication, we have to use HashMaps to keep track of which ReplicationSender your entity is replicated on, even though we should statically know that there can only be one ReplicationSender since you're running in client mode
#

I'm thinking of introducing a global static LIGHTYEAR_MODE with 4 settings: (or it can be implemented as feature flags)

  • Server: you can have multiple connections in your app, but there is one 'logical' server. This also includes HostServer.
  • Client: you will only have a single connection in your app
  • P2P: usually used with input replication only
  • Flexible: current config where anything goes. You could have 2 "client" links each connected to 2 different servers, each replicating a subset of the world, etc.
    Maybe this setting is not necessary since the optimizations are mostly relevant on the send-side of replication, and in this mode you would still only have a single ReplicationSender
#

Then the data structures used could be different based on the global MODE of the app

#

Hm this probably would not work if you want to spawn multiple apps (Client and Server) in the same process..
Maybe the mode can just be stored in a resource

onyx salmon
# pine cape I'm thinking of changing some of the replication internals. Things i'm unhappy w...

Since you're considering moving away from multiple servers and plan to rework the internals, maybe consider using bevy_replicon under the hood?
Sorry - I know I've already asked a few times, but many things have changed since then. We've implemented features that were missing back then, such as prioritization, grouping, etc. I think the only major piece we're still missing is proper delta compression.

The advantage is that you'd get very robust and efficient replication, along with simplified maintenance. You'd also gain a few nice features, such as per-component visibility, serialization specialization, and archetype-based replication rules (for example, you can define (Health, Player) to replicate only when both are present on an entity). I also remember that your messages/events aren't tied to ticks? We have this too: so if the client received a message too early, it will be buffered. This behavior is opt-out per-event.

The downside, I think, is that this would require more rework on your side than you're currently planning. So feel free to say "no" - that's totally okay!
But I feel like a more united ecosystem for networking in Bevy would be awesome.

pine cape
#

Hm I would need more time to evaluate what's missing from replicon or what could be adapted, but I would be down to create a new crate lightyear_replicon that uses replicon (that would replace lightyear_replication). I would probably be keeping lightyear_replication active so that I can experiment with new replication features.

I think the main replication logic is similar between lightyear and replicon:

  • full consistency within one entity
  • distinction between actions (spawn/despawn/insertion/removal) and updates
  • actions are reliable
  • a received update is only processed after all actions prior to that update have been processed

Some of the big things that come to mind:

  • i definitely want client to server replication. The usecase would be to have a world where the simulation load is distributed across client. I have a authority mechanism where you can specify which peer has authority over an entity
  • you support prespawning using Signature, right?
  • I have some special-case serialization for predicted/interpolated entities. A component is inserted/deserialized differently on a Predicted/Interpolated entity. I'm not sure how that could be supported in replicon?
  • How does grouping work? My main usecase is that I want all replication messages for all predicted entities to be sent in a single message.
  • I would like to support P2P settings where a client can be connected to multiple other clients

Also I some other questions:

  • you mention having message/event tied to ticks. Is this done simply by guaranteeing that a message sent by a client at tick T is received on the server at tick T? This works only for client to server messages, no? (since the client timeline is ahead of the server)
onyx salmon
# pine cape Hm I would need more time to evaluate what's missing from replicon or what could...

Yes, I think it's reasonable!

I think the main replication logic is similar between lightyear and replicon:
Yes, I think so.

i definitely want client to server replication
Ah, right, that's another thing that's missing. I can add it if it's important to you.
It's just quite niche - VR is the only real use case I know, and it comes with security caveats.

you support prespawning using Signature, right?
Yep!

I have some special-case serialization for predicted/interpolated entities. A component is inserted/deserialized differently on a Predicted/Interpolated entity. I'm not sure how that could be supported in replicon?
We have a special client markers API to override how a component is written. So you define a marker for predicted and interpolated entities and override how the component is written (for example, "write into component history").
It's separate from deserialization, so users can still have custom deserialization and use it with the markers API.

How does grouping work?
Via relationships. You mark the desired relations as grouping, and internally we build a graph of related entities. We guarantee their changes will be sent in a single message.

I would like to support P2P settings where a client can be connected to multiple other clients
You can have connection, we just provide events and replication. Or you mean that you want clients talk to each other via events?

you mention having message/event tied to ticks
It works for server-to-client messages and is not related to the client timeline. Imagine the server sent replication and a message on tick X. The client didn't receive the replication for tick X, only the message. If tick X contained archetypal changes (such as insertion, removal, or spawn), we buffer the message until the client receives replication for a tick ≥ X.
For example, imagine the server spawned a door and sent a command that references it. If we apply message too early, it will result in logical error.

pine cape
#

So you define a marker for predicted and interpolated entities and override how the component is written (for example, "write into component history").
But how can I guarantee that the Predicted component is added first, before everything else?
I guarantee that right now by adding predicted/interpolated in the spawn message: https://github.com/cBournhonesque/lightyear/blob/main/lightyear_replication/src/message.rs#L75

Ah, right, that's another thing that's missing. I can add it if it's important to you.
It's just quite niche - VR is the only real use case I know, and it comes with security caveats.
I don't think it's only VR. Any bigger open-world game could want the client to participate in the simulation.

How does grouping work?
Via relationships. You mark the desired relations as grouping, and internally we build a graph of related entities. We guarantee their changes will be sent in a single message.
Hm maybe i could work with that. That means I would need to create a 'Prediction' relationship so that all entities that need to be predicted are guaranteed to be one the same group.

I would like to support P2P settings where a client can be connected to multiple other clients
You can have connection, we just provide events and replication. Or you mean that you want clients talk to each other via events?
I just mean that replicon's architecture is very oriented towards 'client-server'.
I think the replicon codebase is very clean, but one thing i really like in lightyear is having separate entities for each link between 2 clients, and being able to customize that link with components like ReplicationSender, ReplicationReceiver.
How would i adapt this with replicon? I'm not too familiar with replicon internals, do you have a Server resource and a Client resource?

GitHub

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

onyx salmon
#

But how can I guarantee that the Predicted component is added first, before everything else?
I think clients know which entities they predict and which they interpolate, is this correct?
So they receive an entity with its initial values first, then mark it as Predicted or Interpolated. All subsequent values will be written differently based on what the client wants.
At least I think that's how @fiery gazelle integrated bevy_rewind. Please correct me if I'm wrong.

Any bigger open-world game could want the client to participate in the simulation.
This is the first time I'm hearing about something like this 🤔 Do you know of any game that does that?
I know it could be used for VR, just to avoid possible rollbacks, since those could cause nausea. At least that's what Joy mentioned when we had a discussion about it.

That means I would need to create a 'Prediction' relationship so that all entities that need to be predicted are guaranteed to be one the same group.
Yep! But why do you need all predicted entities to be in the same group? Is it for lockstep?

I just mean that replicon's architecture is very oriented towards 'client-server'.
Yes, right now we have the ClientMessages and ServerMessages resources. They are used to read and write messages by backends.
And we have ClientState and ServerState, which are Bevy states toggled by the backends. So if ServerState is set to Active, the server starts replication.
If the client becomes ClientState::Connected, it starts processing replication.

#

I can rearchitect this to use entities, but Bevy states are global... And they are very convenient, you get OnEnter/OnExit schedules, in_state conditions and things like StateScoped(ClientState::Connected) (I think it's called differently in 0.17).

Maybe we can have a switch between P2P and client–server and have different states?
I went with client–server because replication in P2P is quite niche (basically, it's for VR). And deterministic replication is already done by GGRS, so I didn't bother 😅
But I'd prefer to foucs client-server for now to polish it and extend it later. It's what most people want to use anyway.

fiery gazelle
unique plover
onyx salmon
unique plover
onyx salmon
#

We don't have a server resource in Replicon. We just have message buffers which are resources that always present. And the activation happens via states that are global.
It's possible to make buffers components and activation via components as well, but states for P2P will be different.

pine cape
#

So they receive an entity with its initial values first, then mark it as Predicted or Interpolated. All subsequent values will be written differently based on what the client wants.
At least I think that's how @fiery gazelle integrated bevy_rewind. Please correct me if I'm wrong.
In lightyear the sender (server) usually adds Predicted/Interpolated to the entity. I found it to be more ergonomic in most cases, since the server knows which client owns the entity, i.e which client should be predicting vs interpolating.
(the client can also add it)

Any bigger open-world game could want the client to participate in the simulation.
This is the first time I'm hearing about something like this 🤔 Do you know of any game that does that?
Take a look at https://docs.coherence.io/manual/authority or https://doc.photonengine.com/fusion/current/manual/network-topologies#shared-authority

Yep! But why do you need all predicted entities to be in the same group? Is it for lockstep?
It's not really for lockstep, I'm just not sure how to make prediction work otherwise.
@fiery gazelle how do you rollback if multiple predicted entities are at different ticks? You just rollback to the earliest of these ticks?
I guess the main risk is that this could cause a wrong re-prediction and more rollbacks, but maybe it would still work?

unique plover
# onyx salmon There is no concept of server in P2P.

Maybe I'm not being clear? I understand P2P doesn't have a server, but when you do have a server could ServerState not exist then?

Maybe you're focusing on internal states for Replicon to use and not end-user states? To be completely clear, P2PStates should probably also be exposed.

#

I'm assuming here that we're gonna have to specify a mode when we create our connection. Either by spawning entities or setting some higher level state to signify Client-Server or P2P.

#

Conditional compilation with features is also a really good option here to select our "mode" and expose the correct states. I can't immediately imagine a use case that includes both P2P and Client-Server architectures in the same app?

fiery gazelle
onyx salmon
#

In lightyear the sender (server) usually adds Predicted/Interpolated to the entity.
In this case, you could probably include a message that instructs the client to add predicted/interpolated values.
During the first replication, the client will receive the initial values as usual, so I think that should be fine.

Take a look
Thanks for sharing!
The first library mentions that shared authority is just easier to use as a reason, which I don’t really like. I hate when devs ignore security in the beginning and fix it later.
But Photon's explanation makes sense to me. When cheating doesn't affect others, mobile devices and the web with limited perfomance and bad connection 🤔
So not as niche as I though.

onyx salmon
onyx salmon
unique plover
onyx salmon
#

Ah, in this case, that's exactly what I meant 🙂

unique plover
onyx salmon
unique plover
# onyx salmon You'll get higher latency. Instead of server->clients you get client->server->cl...

This is the core of what Periwink was arguing though. In the case that cooperative players in an open world setting you don't care about latency from other clients when they're far apart. You want the authority to simulate things near to you so you don't have latency at all.

It seems like you're really focusing on PVP networking, which is fine, but that's the core of why there are currently multiple Bevy networking crates. There are differing use cases.

IMO Lightyear offers the most flexibility at the cost of a (slightly) more complex API. (Although recent advancements like Confirmed<SomeComponent> have really improved that end-user complexity)

Why hasn't Replicon considered using Lightyear as a base instead since you would be considering rearchitecting to entities anyway?

#

To be completely clear, my own use-case doesn't use client authority despite being a cooperative open-world. I do appreciate the ability to choose when to incorporate client authority when I want though.

In our own game the replicated buildable previews could very easily be client authority instead, and I may end up switching to that style if it makes more sense architecturally.

#

In that case latency doesn't matter at all, because the previews are only a visual indicator.

onyx salmon
unique plover
onyx salmon
#

I.e. one of the use cases is to for better performance, that I wrote above.

unique plover
#

I think the mismatch in understanding is that in Lightyear you don't have to have all-or-nothing with server authority. Some can be Client Authority if you want. You can mix-and-match. I might end up making certain systems predicted and others client authority.

onyx salmon
#

I understand how authority works, but I'm not sure how it contradicts what I say 🤔

onyx salmon
unique plover
#

To be clear, I do agree with you that it's a performance choice. And cooperative games can make that performance choice without being on a lower-end platform or VR.

onyx salmon
unique plover
#

I think there's a bit of a language barrier here.

onyx salmon
#

Could be, I'm not a native speaker 😢

onyx salmon
#
  1. Cheating.
  2. Latency for other players.
#

For coop first point doesn't matter. But the second one still counts.

#

If you really need to reduce the load, client authority makes sense. This is why I'm saying it's about avoiding rollbacks.

#

The articles Peri linked explains this nicely.

unique plover
#

Are you trying to instruct me on how client authority works or are you arguing why client authority is niche?

#

I am arguing it isn't niche since coop exists.

#

I'm not trying to instruct at all.

onyx salmon
onyx salmon
# unique plover This is the core of what Periwink was arguing though. In the case that cooperati...

As for why I didn't use Lightyear as a base: I wanted to develop only the replication library I needed for my game.
You might ask why I didn't just use Lightyear, since it provides replication.
It's simple: Lightyear didn't exist back then.

Now you might ask why I haven't abandoned my library.
It's a fair question, given that Lightyear implements things like the mentioned delta compression and shared authority.
However, I think my library is more optimized based on what I saw (and I put a lot of effort into making things optimal) and more robust (simply because my scope is smaller and the library is older - you're unlikely to face a replication bug). And we also have a few other interesting features.

But! Integrating for Lightyear wouldn't be easy and would require losing the mentioned features (at least until I reimplement them, which would take time). That's why I'm explicitly saying it's fine to say no - I just wanted to make sure it was considered, because there are some advantages if we go this way. So there's no pressure, and @pine cape - if along the way you decide it's a bad idea, that's also totally okay.

unique plover
#

Apologies for blowing up the channel y'all!

compact cypress
#

Conversations like these give me faith in the Bevy ecosystem, it was quite informative as well

compact cypress
# pine cape I'm thinking of changing some of the replication internals. Things i'm unhappy w...

the current design allows for a lot of flexibility since any entity can be a Link, and you could have multiple Servers in the same app. However this adds a lot of complexity for not much payoff (i don't think anyone is using multiple servers in the same app anytime soon),

Having multiple servers reminds me of SpacetimeDB, I think because it's mostly relevant when it comes to MMO.

One potential use case of multiple servers could be host-migrations, but that would be a temporary "second" server. Maybe not the best way to do that

#

Perhaps even server migrations if the dedicated server goes down for some reason (e.g. spot interruption)? Just throwing ideas out there

unique plover
# compact cypress > the current design allows for a lot of flexibility since any entity can be a L...

Yeah this is an interesting use-case that I hadn't considered. Another possibility is some kind of high-tenancy dedicated server. You would host many instances of a competitive server on a single running instance of the game binary. You would benefit from shared memory for things like levels (useful when the level layout is the same for many instances e.g. Rocket League or similar) at the cost of potentially taking many instances offline if there's some kind of crash.

#

You would also benefit by having reduced connections to your backend services.

compact cypress
#

+1 for high-tenancy dedicated server. Reduces the need for using an orchestrator like Kubernetes to spin up a container for each session

#

The killer feature that could benefit the most amount of people would be server migrations during spot interruptions, although admittedly that would be complex to pull off (especially in real-time).

onyx salmon
onyx salmon
unique plover
# onyx salmon I don't think you need to spawn multiple servers for it. Why open multiple socke...

Well multiple sockets could be an incremental upgrade if you were moving from a single-tenancy model to a multi-tenancy one. Your matchmaker wouldn't need to know the difference and you're theoretically only changing the game server code to support it and not necessarily the client side.

I don't think this is a particularly strong argument in favor of multiple server entities, just a potential use case since the feature exists now.

But yeah, in a fresh environment, I think you could probably manage using rooms assuming you can segment rooms if you're using them per instance too.

#

Oh I guess another scenario is that you're using an off-the-shelf matchmaking service that is expecting different ports per host.

onyx salmon
pine cape
# unique plover Apologies for blowing up the channel y'all!

no worries, best way to learn is by having these kinds of conversations.
I actually have a question about replicon. How does the server replicate messages to the clients?
Is it done in a single-threaded way where all messages are written in a single buffer, and then the final message for each client is built from that using index ranges into the big buffer?
Can a message be serialized once and then the bytes be re-used across clients? That's what I had in a previous version of lightyear but I switched to a parallel approach where the messages for each client are written independently in parallel. Not sure what's better

onyx salmon
pine cape
#

And the client doesn't re-use the range if the data potentially contains entity-mappings?

compact cypress
onyx salmon
# pine cape And the client doesn't re-use the range if the data potentially contains entity-...

Everything is re-used except removals and despawn caused by visibility changes (because it's client specific). But we serialize server entities inside components as is.
Clients map the entities during deserialization. If it's a new entity, it will be spawned and the mapping will be remembered. If it was previously spawned, the data goes into the existing entity.
For signatures, they are simply located at the beginning of the message, so the client deserializes hash<->entity and maps them before processing the rest of the data.

As for performance, I haven't tried multiple threads, so I can't say for sure.
But I found that writing to a single buffer sequentially is faster than using many small buffers for different things. And replication systems don't require full world access, so they could run in parallel with other logic.

pine cape
# onyx salmon Everything is re-used except removals and despawn caused by visibility changes (...

That make sense; I had something similar but I had to move away from this to support bi-directional replication without code duplication.
There's so many choices and trade-offs 😅

But yeah i'm down to try to integrate with replicon, but it's not really on my list of priorities. I'd be open to PRs opening a lightyear_replicon crate that replaces lightyear_messages, lightyear_replication.
The absence of delta compression doesn't matter at all, it's pretty niche

onyx salmon
#

Interesting, I wonder I'll be able to keep this approach 🤔

Glad you like it!
But same here - there are many other things in my priority list right now... I feel like I never make my game this way 😅
Maybe you could open an issue in case some brave soul decides to tackle it?

random palm
#

is lightyear syncs entity ? for example if i send a message containing entity, can i use it on server to find the same entity ?

pine cape
#

yes, entities are mapped within messages

split violet
# pine cape yes, entities are mapped within messages

within components as well, right? using the EntityMapper(if it was called that). I haven't used lightyear for a few weeks and it' used to move/iterate fairly quickly xD
With this in mind: There is barely much differentiation between developing a networked and local (not networked) game with lightyear because entity mapping of messages and components is possible. That seems fairly nice

cloud patio
#

Is it possible to have a Player entity where some components are server-authoritative (Position, Transform) and others are client-authoritative (PlayerName)?

Use case: Client should be able to update their own username, which then gets rebroadcasted to all other clients. Right now I'm using a message-based approach(client sends UpdateUsernameMsg -> server iterates over player entities to find associated player -> server updates PlayerName -> replicates to all), but it requires inefficient entity relationship lookups.

Ideal flow would be:

  1. Client inserts/updates PlayerName on their predicted player entity
  2. Server accepts the client-authoritative component update
  3. Server replicates to all other clients

If this mixed authority isn't possible, what's the recommended pattern for client-initiated updates to replicated entities? The current message-based approach works but feels clunky when the client just wants to "set a field" on their own player.

pine cape
#

Currently i think the message-based approach is correct.
The mixed authority idea is interesting, but it's not really a priority right now

random palm
#

hello i'm trying to write a server implementation without enabling rendering, and i'm using gltf files to spawn a scene

#

how can i do it without rendering the server ?

pine cape
random palm
#

@pine cape when i spawn a gltf scene with ColliderHierarchy, some of the meshes in the scene starts rotating

#

I think something is wrong with the lightyear physics

pine cape
#

What replication mode are you using

random palm
# pine cape What replication mode are you using
app.add_plugins(lightyear::avian3d::plugin::LightyearAvianPlugin {
            replication_mode: AvianReplicationMode::PositionButInterpolateTransform,
            ..default()
        });
        app.insert_resource(avian3d::physics_transform::PhysicsTransformConfig {
            transform_to_position: true,
            position_to_transform: true,
            ..default()
        });

        app.add_plugins(
            PhysicsPlugins::default()
                .build()
                // disable the position<>transform sync plugins as it is handled by lightyear_avian
                .disable::<PhysicsTransformPlugin>()
                .disable::<PhysicsInterpolationPlugin>()
                .disable::<IslandPlugin>()
                .disable::<IslandSleepingPlugin>(),
        );
random palm
pine cape
#

I believe PositionButInterpolateTransform doesn't work well with entity hierarchies unfortunately..
Could you try using AvianReplicationMode::Transform instead?

random palm
pine cape
random palm
#

oh okay thank you

pure drum
#

I've been getting these on the server. It still sees input but doesn't apply them.

2025-12-16T00:42:54.807149Z DEBUG lightyear_inputs::input_message: Mismatch detected at tick Tick(19) for new_input Some(ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }). Previous predicted input: None

... etc (every tick)

2025-12-16T00:43:01.907398Z DEBUG lightyear_inputs::input_message: Mismatch detected at tick Tick(161) for new_input Some(ActionsSnapshot { state: Fired, value: Axis2D(Vec2(1.0, 1.0)), time: ActionTime { elapsed_secs: 0.85000014, fired_secs: 0.85000014 }, events: ActionEvents(4) }). Previous predicted input: None

... etc (every tick)

2025-12-16T00:43:03.507339Z DEBUG lightyear_inputs::input_message: Mismatch detected at tick Tick(193) for new_input Some(ActionsSnapshot { state: None, value: Axis2D(Vec2(0.0, 0.0)), time: ActionTime { elapsed_secs: 0.0, fired_secs: 0.0 }, events: ActionEvents(0) }). Previous predicted input: None

How can I debug this myself? It seems related to my past issues (Axis2D vs bool/inputs not coming through/inputs on server applying old input even when let go or changed direction).
I'm using ly main branch.

pine cape
#

These are not bugs and are completely expected.
Basically the client predicts that every other clients keeps pressing their last pressed input.
If another client changes their input, then you would get a mismatch compared to what you were predicting for them, which causes a rollback

junior ember
#

I was playing around with the "avian_physics" example in lightyear to get a feel for the library and the unmodified example always eventually crashes after I play around with two clients for a while. Sometimes, the server crashes with the same message too, but this time it survived.

Client crash logs: https://pastebin.com/SBgtAtr0

thread 'main' (41699) panicked at /home/wurst/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/avian2d-0.4.1/src/dynamics/solver/islands/mod.rs:650:9:
assertion failed: island.contact_count > 0

When I tried out another crate for networking, bevy_rewind (using replicon), I saw that they disabled the island solver:

app.add_plugins(
        PhysicsPlugins::new(SimulationPostUpdate)
            .build()
            .disable::<IslandPlugin>()
            .disable::<IslandSleepingPlugin>(),
    )

I did not experience these crashes in the toy_cars example from bevy rewind. Does anyone know more about this? Should I disable the island solver for my own game, or is this crash caused by something else?

pine cape
#

It's an oversight in the lightyear example; the island plugins need to be disabled to work properly.
The reason is that avian maintains some internal state for the island plugins that are not updated properly on rollback and cause crashes

stray sinew
#

Replicate::handle_connection was matching on replicate.mode even when handling PredictionTarget / InterpolationTarget; using the passed-in mode fixes cases where entities were incorrectly marked as both predicted and interpolated, which caused invalid spawn actions and a serialization crash. I’ve opened a PR with the fix.

shrewd granite
#

So I think due to rewrites or something this kind of log spam is happening to me.
https://github.com/cBournhonesque/lightyear/issues/322#issuecomment-2108675797

After looking into it and looking through the discord it seems to be a windows issue as mentioned here #1189344685546811564 message
I can open an issue on github if needed. The fix could either be would @long marsh mentioned OR the UDP socket on windows has to be created with the SIO_UDP_CONNRESET flag set to false I think?
https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls#sio_udp_connreset-opcode-setting-i-t3.
I guess this can also be handled by removing the peer who is being set to?

GitHub

When one half of a connection is forcibly terminated (application shut down or crashes), the other side will spam log messages. Client shut down without disconnecting from the server: 2024-05-08T01...

Navigation topic for Windows Sockets (Winsock) socket IOCTLs.

#

Another issue is that because the error originates in the receive function in recv_from it gets to it, breaks, and just stops receiving messages right?

#

Have I mentioned that I hate Windows

pine cape
#

I don't have a windows machine so it's hard for me to test, but yes please open an issue!

dreamy silo
#

how do one get a client only camera thats frame interpolated using lightyear avian? now its kinda living its own life again. seems to be just 1 game loop behind? I do want it to be client only, so I guess i just gotta put it in the right schedule with with the right after/before conditions? everything else seems to work very smoothly. I did have it working well some time back but i just teleported from bevy 0.14 to 0.17 recently, so i think some of the stuff I did correctly previously has been lost in new changes. usually it was on following avian physics to have it synced but not i see it relies on lightyear_avian. Right now my shared player_movement and client camera movement is on the schedule FixedUpdate.

#

camera system runs like this: move_camera.after(shared::player_movement) in the FixedUpdateschedule. am i thinking correctly?

pine cape
#

No camera should be in PostUpdate, after TransformPropagate. The camera should be updated on every frame. And if the camera is following an entity that is updated in FixedUpdate, the camera value should be set to follow that entity after FrameInterpolation is applied

molten socket
#

Hello, I'm currently developing a voxel sandbox game with a server-client architecture and I'm having a hard time organizing my code.
Some components are server-side only, others are client-side only, some are shared, others are shared and replicated, and others are shared but not replicated. I think that, unlike your examples, I will not separate the rendering code, because only the client will perform the rendering, so there is no need to see the server's point of view (this may be a bad idea, please correct me if I'm wrong).
I have thought of these two ways to structure my project:

#1

.
└── features
    ├── feature1
    │   ├── client.rs
    │   ├── mod.rs
    │   ├── protocol.rs
    │   ├── server.rs
    │   └── shared.rs
    ├── feature2
    │   ├── client.rs
    │   ├── mod.rs
    │   ├── protocol.rs
    │   ├── server.rs
    │   └── shared.rs
    ├── feature3
    │   ├── client.rs
    │   ├── mod.rs
    │   ├── protocol.rs
    │   ├── server.rs
    │   └── shared.rs
    └── mod.rs

#2

.
├── client
│   ├── features
│   │   ├── feature1.rs
│   │   ├── feature2.rs
│   │   ├── feature3.rs
│   │   └── mod.rs
│   └── mod.rs
├── protocol
│   ├── features
│   │   ├── feature1.rs
│   │   ├── feature2.rs
│   │   ├── feature3.rs
│   │   └── mod.rs
│   └── mod.rs
├── server
│   ├── features
│   │   ├── feature1.rs
│   │   ├── feature2.rs
│   │   ├── feature3.rs
│   │   └── mod.rs
│   └── mod.rs
└── shared
    ├── features
    │   ├── feature1.rs
    │   ├── feature2.rs
    │   ├── feature3.rs
    │   └── mod.rs
    └── mod.rs

In the first structure, I think it is easier to navigate and maintain each feature, while the second structure allows each top-level module to be converted into a crate.
Do you have any suggestions? There are few open source games that use lightyear, and even fewer that are well organized.

pine cape
#

I think both could work and it's mostly a matter of preference! I mostly use option 2

strong barn
#

@pine cape I noticed that when input locks up, I am getting new errors on 0.25:


2025-12-28T19:59:31.268054Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.333822Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.399574Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.464628Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.531183Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.596866Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.662778Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.730063Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
2025-12-28T19:59:31.794167Z  WARN bevy_enhanced_input::action::fns: action `PlayerMovement` (`237v1`) expects `Axis2D`, but got `Bool`
#

Very strange since PlayerMovement is not a bool. Why would it be broadcasting as a bool?

use bevy::prelude::*;
use bevy_enhanced_input::prelude::*;

#[derive(Debug, InputAction)]
#[action_output(Vec2)]
pub struct PlayerMovement;
pine cape
#

That's a quirk with BEI where by default the inputs are replicated as 'bool'; normally it should only happen a few times, until the first time the real input value gets replicated

unique plover
# molten socket Hello, I'm currently developing a voxel sandbox game with a server-client archit...

The way that I've organized my project is pretty different from these.

.
└── src
    ├── bin
    │   ├── client.rs
    │   └── dedicated_server.rs
    ├── core
    │   ├── physics.rs
    │   ├── protocol.rs
    │   └── mod.rs
    ├── data
    │   ├── actions.rs
    │   ├── components.rs
    │   ├── events.rs
    │   ├── states.rs
    │   └── mod.rs
    ├── systems
    │   ├── feature1.rs
    │   ├── feature2.rs
    │   ├── feature3.rs
    │   └── mod.rs
    └── lib.rs

Source files in data have this kind of structure:

// Anything at this level is server + client
pub struct SomeComponent;

#[cfg(feature = "client")]
pub use client_only::exports::*;
#[cfg(feature = "client")]
pub(super) mod client_only {
    use super::*;

    pub mod exports {
        // Anything at this level is client only
        use super::*;

        pub struct SomeClientComponent;
    }
}

Source files in systems have this kind of structure:

use crate::prelude::*;

pub(super) fn plugin(app: &mut App) {
    app.add_plugins(some_system);
}

// systems and observers at this level are shared server + client
// and can be scheduled differently in the client module and using run conditions

fn some_system(...) { ... }

#[cfg(feature = "client")]
pub(super) mod client_only {
    use super::*;

    pub fn plugin(app: &mut App) {
        app.add_plugins(some_client_system);
    }

    fn some_client_system(...) { ... }
}

let me know if you need more elaboration with the way the mod files are structured to set up the module visibility and plugin loading, but this hopefully gives you enough to determine if it's a good organizational pattern for you.

plucky smelt
#

Hi, I'm brand new to lightyear, still wrapping my head around things.

What I want is for my server to tell the client what map to load, then the client to wait for that map to load, and then for replication to start. So should I have the server send a "greeting" message to the client, and then the client to send a "loaded" message back to the server once it's ready, and then enable replication for the client or something?

Is there a better way to do this?

random palm
#

how can i replicate positions of a mass amount of entities with small bandwidth ?