#lightyear

1 messages · Page 4 of 1

wintry dome
#

it's the predicted entity i actually want to control, so i think it would make sense to copy Controlled to the predicted..

pine cape
#

maybe it needs ComponentSyncMode=Simple in case the Controlling player keeps changing (could be the case for some games), but i think this is a good start

wintry dome
#

cool, yeah that cleans up my code to setup newly spawned players thanks

#

weird issue where if i tap eg Left it appears that the actionstate for left remains pressed for far too long, i think as long as the server_send_interval

pine cape
#

This happens also for the main player, not for predicting remote players, right?
It's a known issue, it's because I only send diffs for inputs. The ReleaseLeft diff probably didn't get applied on the server so the server still thinks you're pressing Left. It actually would last indefinitely until you press Left again (and this time the server receives the ReleaseLeft message).

What is your link conditioner config? I think this might happen more at lower latencies.
For now you can set send_diffs_only to false so that this doesn't happen: https://docs.rs/lightyear/latest/lightyear/client/input_leafwing/struct.LeafwingInputConfig.html#structfield.send_diffs_only

#

Also your client_send_interval=Duration::default() right? client needs to send input messages every frame

wintry dome
#

i'm just testing with a server and a single client atm, let me double check my send intervals

#

yeah, set to this atm: client_send_interval: Duration::default(), server_send_interval: Duration::from_millis(1000),

#

wouldn't it still send a diff once i release the key? one diff when i press it, one when i release?

#

or if that happens within one send interval perhaps not

pine cape
#

Oh so your issue is actually that left remains pressed for server_send_interval? it doesn't stay pressed indefinitely?

pine cape
# wintry dome wouldn't it still send a diff once i release the key? one diff when i press it, ...

Yes but i think there's a chance that the input for tick T arrives on the server when the server is already at tick T+1. (the change is pretty low, because I take some margin for the tick difference between server and client. Something like RTT + 3 * jitter, but it's possible). I think this can be fixed by not sending raw diffs, but instead ActionState for tick T, and then diffs for the next 10 ticks (as you suggested earlier)

wintry dome
#

the client is seeing my Left key as pressed for a whole second (~64 ticks) even tho i just tapped the key. looks like the server only sees it pressed for 2/3 of a second or so, weirdly. still investigating

#

i'll turn off diffs and see what happens

#

fwiw, server link condirtion is None, client link conditioner is:

            conditioner: Some(Conditioner(
                latency_ms: 75,
                jitter_ms: 10,
                packet_loss: 0.02
            )),
#

disabling diffs means the server sees the correct inputs for each tick, client still misbehaving. will investigate more later, thanks!

pine cape
#

I tried it on the bullet_prespawn example with send-interval = 1000ms but it worked for me

bronze elm
# pine cape Created https://github.com/cBournhonesque/lightyear/issues/396

I think we may need an example that uses server spawned players for testing because there are still quite a few odd bugs with that. I would love to switch off of my convoluted “interpolating yourself” method but I have tried mutiple times unsuccessfully to switch it to predicted like what RJ is doing. I could be doing something wrong but I think I have largely narrowed the problem down to the leafwing inputs. (Referring to the hammering/jitter + desync + inputs held down and never released)

wintry dome
wintry dome
pine cape
wintry dome
#

but a couple of issues. seems like the ball is partially replicating to clients, i see the entity but it's missing crucial components like BallMarker, not sure how that happened. it's still in the protocol

#

the janky movement/input is fixed though, per my comment on the PR earlier

#

the ball was always spawned on the server in the xpbd demo too, so i've no clue how i've managed to break that

#

using the server gui i can crash into the ball, and see the client gets updated LinearVelocity for it, but somehow missing other components

#

same issue when new players join - they get incomplete replicas of already-connected players. any idea what's going on?

pine cape
#

You're on lightyear main? I might have broken some of the replication logic

wintry dome
#

yeah on main

#

ah, yeah. works fine if i run my demo atop 303a2f00201a0e55bdd84fb940e955ee47387187 (and pick the sync-controlled commit)

pine cape
wintry dome
#

yep that fixes my issues, thanks

bronze elm
# pine cape this all seems to be unrelated to pre-spawning players no? once the prespawning ...

I’m not sure. I don’t fully understand how the interpolated version with 100% server side physics works fine but running physics on both with a shared movement behavior, acting on the predicted entity, causes jittering/bouncing. I’ve tried everything i can think of. Also, I think i tried using the visual interpolation plugin but wasnt able to use it because I think position/rotation needed some function implemented which I cant cuz they are xpbd structs. I should update the networked cube test to demonstrate when i have some time but ive been writing shaders for 3 days and im brain dead 🤪🔨

pine cape
pine cape
wintry dome
#

I want to experiment along those lines - will probably add shooting. keeping it basic because I want to review some rollback edge cases (and maybe add more diagnostics stuff)

bronze elm
# pine cape What's the best way to provide hooks for user logic? I have this PR: https://git...

This is an example of how a minecraft plugin would work for whitelisting based on name:

https://github.com/josantonius/minecraft-whitelist/blob/main/src/main/kotlin/dev/josantonius/minecraft/whitelist/Main.kt#L28

That might be one idea, like if instead of a closure there was a trait like, ConnectionHandler with on_connection_attempt that is provided an event with some context (maybe even socket info like IP address). I think the boolean is ok but if you do end up changing the DeniedPacket to include a status flag, this handler could return an Option with some denial reason

GitHub

A basic whitelist plugin based on player nicknames only - josantonius/minecraft-whitelist

#

and the default implementation would be just to accept all

pine cape
#

Yes eventually we can have Denied reasons, but i want to start with the simplest impl.
Thanks for the link

stiff quiver
#

maybe this would make more sense in a system

#

ah also, shouldnt this be done on the server that creates netcode connection tokens anyways?

pine cape
stiff quiver
#

but idk i feel like it should be handled when creating the connection tokens, not when the user is already connecting imo

stiff quiver
#

hmm

#

might have overlooked smth

#

lemme check

#

@pine cape figured it out, was a typo in lastest version, ill fix and publish a new version

pine cape
#

maybe 0.3.1

stiff quiver
#

v0.3.1

#

released

#

@pine cape u can just use new version, no changes required on ur part

pine cape
#

works

jade ember
#

@dull lion ( @pine cape )

Steam p2p works now :)

took me a moment to get a good understanding of the codebase and how everything works, and then another good while to spot the little error you missed @dull lion

when the client connects, you never actually set the connection field inside the Client struct!
After doing so, everything works perfectly without any additional changes :)

I currently just have my steamID hard coded into the project.... better would be to have a nice way to be able to specifiy what player you'd join, or have the steam invites work, but for testing purposes this works for now

it's a bit hard to record two different devices, so you just need to believe me that :P

#

I noticed a few other problems, though i'm unsure if it has something to do with the steam implementation or maybe generally host-server mode.

if i host, and the client joins and then leaves again, he seems to be unable to join again. i have to close the lobby and open a new one.

this is something i'll have to look at later, as it's quite late for me rn and i need some sleep lol

#

i'll quickly create a merge request with the fix, altough its nothing big

pine cape
# jade ember I noticed a few other problems, though i'm unsure if it has something to do with...

I think that's related to the steam; I created an issue for this in the past: https://github.com/cBournhonesque/lightyear/issues/243

GitHub

Issues with NetworkingConfigEntry There are several issues with NetworkingConfigEntry: Noxime/steamworks-rs#168 Noxime/steamworks-rs#169 Issues when recreating the ClientConnection I tried dropping...

pine cape
jade ember
#

And the host is just the "Server" which will receive all the messages and will Act as Server and forwards them to the others

#

Sorry if the explanation or my language isnt good, i'm really tired XD

#

Currently almost 4am for me :)

jade ember
#

And it opens up doors to also use the steam invites via the steam overlay

#

Allowing Players to easily invite each other without much trouble

#

And i'd say because of this whole thing, it only really makes sense in server-host Mode, and not in any of the other ones

pine cape
#

Ok thanks for the summary!

jade ember
#

Of course!
Now if you excuse me, i'll go and Pass out in my bed. Work starts in a few hours again 🥲

pine cape
#

Aha of course; good luck 💪

bronze elm
#

Update on rust in rust.... got animation state networked + hooked up to bevy-tnua character controller, vegetation shader, modular characters fully networked as well (including partial updates like replacing just the torso if the wearable changes), added networked deployables and hooked it into the inventory UI so you basically join/leave the room when interacting with the thing

pine cape
#

Love getting those updates; looking great!

wintry dome
#

added shooting to the spaceships example in my fork, using PreSpawnedPlayerObject
i've a feeling there are still too many rollbacks, although it's mostly making sense i think

#

it will probably glitch if two players both fire in the same tick, unless i manually specify a hash for PreSpawnedPlayerObject (?) since the bullet archetypes are the same regardless of who fires it

#

i noticed that if a client spawns with PreSpawnedPlayerObject but the server doesn't spawn a matching one, the client auto despawns after a while. is that timeframe configurable, or keyed to latency or something?

#

if i spawn bullets with a TTL {despawn_at_tick: Tick} component, is it safe to despawn in client and server systems, or will the server be upset if the client removes confirmed/predicted entities itself? ie, the server sends a "despawn this bullet" update, but the client has already despawned it. does that just silently do nothing

#

hm, watching that video back i notice a bug.. the remote players are spawning a PreSpawnedPlayerObject bullet because of their inputs, and receiving a replicated one from the server at the same time i think.

#

but i do want the server to replicate bullets, since if a player joins i want them to receive bullets that are currently in-flight, can't rely on receiving PlayerAction::Fire to spawn them, could have happened prior to joining the game

#

in some cases you'll have player inputs and know remote players want to fire before the server replicates their bullet to you, but not always - latency/input_delay depending 🤔

dull lion
wintry dome
#

I suppose I could hide it on the client and remove the colider safely, let the server clean it up

pine cape
pine cape
# wintry dome i noticed that if a client spawns with PreSpawnedPlayerObject but the server doe...

The client despawns after a period related to the interpolation timeline (at the interpolation timeline we should have received the server's message): https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/client/prediction/prespawn.rs#L334
It's not configurable currently

GitHub

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

wintry dome
#

hmm. i would like players joining mid-game to see all the bullets in-flight as they join. so i think they need to be replicated. perhaps there's a more efficient approach though

pine cape
wintry dome
#

yes i'd like to make a PR, want to get shooting working well first

wintry dome
pine cape
#

I had a command to do that in version 0.14.0 to guarantee that you could atomically remove Replicate and despawn the entity.
(otherwise it only works if there is one frame of delay between Replicate being removed, and the entity being despawned)
I removed it because I wasn't pleased with the ergonomics but I should add it back

GitHub

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

#

hm, watching that video back i notice a bug.. the remote players are spawning a PreSpawnedPlayerObject bullet because of their inputs, and receiving a replicated one from the server at the same time i think.
but i do want the server to replicate bullets, since if a player joins i want them to receive bullets that are currently in-flight, can't rely on receiving PlayerAction::Fire to spawn them, could have happened prior to joining the game
in some cases you'll have player inputs and know remote players want to fire before the server replicates their bullet to you, but not always - latency/input_delay depending

I don't understand this part. Client 1 fires. You're saying Client 2 is spawning a PreSpawnedPlayerObject bullet (for the bullet fired by client 1), but that is too late because it receives the client 1 inputs with a delay? So it could receives the server Replicated bullet at the same time or before the 'client 1 fire' input?

In that case you would want the Replicated bullet to not get despawned (because It doesn't find a pre-spawned object), but instead to just be replicated? (and when it's replicated, it creates a Predicted bullet on the remote client)?

pine cape
wintry dome
#

seen around 0:12 in the vid, when pink fires

dull lion
pine cape
#

I see.

  • case 1: either the ActionState is replicated first, then it spawns a bullet. If the server's Replicated bullet arrives after, it should just merge with the existing bullet. This seems to work properly.
  • case 2: either the replicated bullet arrives first so we spawn it on player 2. Then we receive the ActionState bullet afterwards, which spawns another bullet.

I think for case 2 I could just merge the ActionState-spawned bullet with the replicated bullet.
(i.e. we do the merging both in the ServerEntity merges with PreSpawnedObject and PreSpawnedObject merges with ServerEntity directions)

pine cape
wintry dome
# pine cape I see. - case 1: either the ActionState is replicated first, then it spawns a bu...

i think case 2 is what's causing an extra bullet to appear yes. i need to deterministically create the hash for the PreSpawnedObject (based on the (playerid, tick) it was fired at), but assuming i assign that hash on the server-created bullet, i want to avoid doing a spawn on the client because of a Fire ActionState if the PreSpawnedObject hash already exists on an entity.

this means if the client receives a newly replicated object from the server with a PreSpawnedObject, but there is no existing entity with a matching PreSpawnedObject.hash, it should keep the PreSpawnedObject component for a while, to permit the client to detect it and bypass spawning something. is that how it works atm?

dull lion
pine cape
#

Well right now the matching only works in the other direction. If a client entity with PreSpawnedObject exists, we try to match it with incoming server entities that have PreSpawnedObject. If we can't match within a given window, we despawn it.

But yes we might want to do the opposite. If the replicated entity with PreSpawnedObject is spawned first, then if we create a client object with PreSpawnedObject we can try to match with existing entities that have PreSpawnedObject.
Currently the PreSpawnedObject component of replicated entities is removed immediately after the initial matching attempt, but I can keep it around for this usecase.

wintry dome
#

yes I was wondering if something like that would make sense.

pine cape
#

One last thing, actually this bug happens 100% of the time in your situation.

You're predicting all bullets and clients, so they should all be in the same replication group. (otherwise the rollback logic wouldn't work properly because bullets or players would be in a different tick)

So the ReplicatedBullet arrives at the same time as the "Fire" ActionState : they always arrive in the same packet on client 2. Which means that the ReplicatedBullet is always created first (since the receive-replication logic runs in PreUpdate), whereas the duplicate bullet should be created second. (it runs in your FixedUpdate 'spawn_bullet' system)

#

That's why every single shot seems to do double-bullets

#

Then it might make sense to only keep the PreSpawnedObject component on the replicated entity only for 1 tick?

wintry dome
#

1 tick should always be enough I think

pine cape
#

also i'd like to understand why i've never seen this in the bullet_prespawn example, let me test it now

wintry dome
#

should I check for it manually before spawning, or should I spawn anyway and lightyear magically removes it if the hashes match?

#

ok

pine cape
wintry dome
#

yes ok, feels like something that should be taken care of if I use prespawnedobject

pine cape
#

ah i've never seen this because in bullet_prespawn the remote players are interpolated, not predicted

wintry dome
#

ah, makes sense

jade ember
#

whenever you call the connect api, it first inits the relay

#

that's what we're currently doing

jade ember
jade ember
dull lion
pine cape
#

Btw what's the status of the PR? let me know when I can review

dull lion
#

Once @jade ember submits his fix I'll do a cleanup pass and then I suppose it'll be good to go

jade ember
dull lion
#

huh weird, no notification about it

jade ember
#

no worries!
and i mean its just one line change

#

so the merge request wasn't even really needed tbh

dull lion
#

credit where credit is due!

#

i'll have a look at this again later tonight or tomorrow then

stiff quiver
#

@pine cape what do u think would happen if we use listen server mode (iirc thats what u call it when the server runs in a local thread), does the web keep alive still work (aka does it tick the server?)

pine cape
stiff quiver
#

thought it was like a sub app or a second app

jade ember
#

@dull lion i can't seem to find any way to actually be able to use the steam invites when using the p2p socket listener.

only way, as far as i can tell, is by using the lobby functionality of steam.
do you know any more?
otherwise maybe it'd make more sense to actually use that instead of the socket, or maybe support both variants. generally, the matchmaking/lobby path might make more sense, i think, as it generally provides more functionality and has better support...

#

locally i already experimented a bit with lobby.
it's a bit annoying, as we'd have to work quite a bit with Channels as steam uses callbacks for creating/joining lobbies, but otherwise i think it should mostly work with the current setup

dull lion
#

I don't think it's an either/or thing. You'll be using the p2p sockets both ways (at least that's what the renet steam example does if I'm reading it right)

jade ember
#

actually you don't no.

you just init the relay network access, create the lobby... and you're ready to go.

you can then query for all lobbies and list them (you can create them private, friends only, public etc) and then join them.
and inviting/joining friends immediatly works as well.

but no p2p socket required

#

it still goes over the steam relay of course

dull lion
#

I mean, the lobby is just there to mediate setting up the steam p2p socket right? It's not a networking library of its own

jade ember
#

hm, yes? i think?
what you can do is just use the lobby (or matchmaking as steamworks actually calls it) to setup everything and connect everyone with each other, and then once they're connected you can just send messages around however you want.

so what we could actually do is the server/host creates a lobby, and with that everyone can join easily via overlay, and from there, i think, we can just use the connection...

#

it's a while since i worked again with steamworks networking

#

so need to look at again how exactly they're connected

jade ember
#

got a bit late yesterday :)

pine cape
#

yeah I just forgot to make them pub

jade ember
#

currently trying to run the simple_box example, with the newest changes in main.
currently unable to compile because it complains at the start_server function:

/// Start the server
fn start_server(mut config: ResMut<ServerConfig>, mut commands: Commands) {
    for net_config in &mut config.net {
        net_config.set_connection_request_handler(Arc::new(|client_id| {
            client_id != ClientId::Netcode(0)
        }));
    }
    commands.start_server();
}
error[E0599]: no method named `set_connection_request_handler` found for mutable reference `&mut lightyear::prelude::server::NetConfig` in the current scope
  --> examples\simple_box\src\server.rs:34:20
   |
34 |         net_config.set_connection_request_handler(Arc::new(|client_id| {
   |         -----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `&mut NetConfig`
#

I saw the function got renamed from set_accept_connection_request_fn to set_connection_request_handler but for some reason it can't find it

pine cape
#

ah sorry you can just delete all that

#

let me push a fix

#

done

jade ember
#

awesome, thanks :)

jade ember
#

@dull lion sorry for the delay, just created a merge request on your branch updating the simple-box example to correctly support the p2p changes and i just successfully tested everything, the example seems to be working without issues :D

#

@pine cape you can also already merge his pull request and i just create a new one in which i update the example on lightyear, the merge request itself from @dull lion should be all good to be merged

dull lion
#

@jade ember maybe easier if you do that in a follow-up pr to lightyear

#

great news that the examples are working!

wintry dome
#

i have a situation in my fork's spaceships demo where with two clients connected, when one client presses fire, the other client sees log msgs like this:
ERROR lightyear::client::input::leafwing: received input message for unrecognized entity entity=16v2
where that entity (16v2) doesn't exist. i'm not sure if i've done something wrong, or if there's a bug on main atm.

pine cape
#

Your ships are not PrePredicted entities right? they were created on the server?
Do you see these logs every time?

wintry dome
#

i think this is happening reliably every time

#

just going to rebase upstream and retest

pine cape
#

And the server is rebroadcasting the player inputs? i tested it in leafwing inputs with pre-predicted player entities, but maybe it doesn't work in this case. I'll test on your branch later

wintry dome
#

hm, not happening on the first client i launched, but the second client still spits the errors when first client presses keys

#

server is also saying ERROR lightyear::shared::replication::send: Received an update message-id nack but we don't know the corresponding group id

#

i based my demo off the leafwing/xpbd one, so it has the same rebroadcast code as that

#

i'm testing by running cargo run server and cargo run client -c 1 and cargo run client -c 2 fwiw

pine cape
#

sg

#

@dull lion @jade ember I merged the steam p2p PR, looks great to me; thanks for the change!
I was just wondering if the Arc<RwLock>> is necessary since the client is apparently thread-safe

#

@wintry dome would you mind giving me read access to your fork?

wintry dome
#

the spaceships demo is in my main branch at the mo

pine cape
#

ah right idk i got confused by some git errors

wintry dome
#

ok

dull lion
#

So do you think it would be possible to use this as a wrapper to the user's provided SteamworksClient to avoid the Arc<RwLock>?
We could maybe bring back your wrapper there, though I think I had a good reason for removing it. Can't remember. Maybe not

pine cape
#

@wintry dome fixed both logs. The first one (the input one) was a real issue; rollbacks should have a bit better precision now because they use remote inputs

jade ember
wintry dome
#

i'm trying to understand how interp/corrections work when everything is predicted (ie, client simulates all entities in same timeframe ahead of server).

#
// this will snap the component to the new value if a rollback was needed.
// if we render the Predicted entities, clients will see the entity warp position instantly.
app.register_component::<Position>(ChannelDirection::Bidirectional)
.add_prediction(ComponentSyncMode::Full);
#

is the following correct? ie, the Predicted entity's Position is gradually changed as part of interpolation, to account for difference pre and post rollback?

// in the event of a rollback needing to change the Position component, it will gradually lerp the
// Position component of the Predicted entity over `correction_ticks_factor` ticks, instead of snapping.
app.register_component::<Position>(ChannelDirection::Bidirectional)
.add_prediction(ComponentSyncMode::Full)
.add_interpolation(ComponentSyncMode::Full)
.add_interpolation_fn(position::lerp);
#

so if i render the Predicted entity, its Position will be more smoothly blended to conceal rollback discrepencies?

#

the Correction component is used to store the pre rollback component value to use for lerping to smooth sudden changes due to rollback prediction errors, but only if you set a correction fn(?)

#

what I want to ensure is that when eg the position needs to change due to a rollback, i want to immediately snap the Predicted entity's position to the new value, because otherwise it will just result in more rollbacks, since the Predicted entity is the one being simulated by the physics engine. However, i do want to use the correction data between pre/post rollback to visually blend the position i use for rendering.

wintry dome
#

i'm able to get the effect i want, sort of, by setting a correction_fn which just snaps to the final value, purely so the Correction component is added. Then I take the correction.original_prediction value when i detect Added<Correction<C>>, take a delta between that and the current component value, and decaying that and applying it to the value i use to render.

pine cape
#
  • Interpolation is only used for non-predicted entity, and it's just to visually interpolate components between the server-snapshots. It has no effect on predicted entities
  • Correction does what you want: it lerps the snapback smoothly over a few ticks. To enable it you need to add a correction fn (or add_linear_correction) in the protocol. Correction does exactly what you say: the Predicted value is snapped instantly to the post-rollback value, but correction will override that value during a few ticks in PostUpdate to visually blend between the current Prediction and the new post-rollback value. (in pre-update correction will set the Predicted value to the post-rollback value again to avoid more rollbacks).

You shouldn't need to change anything on top of corrections. It already only visuall corrects at render time, and swaps back the actual real value in PreUpdate

wintry dome
#

ah! thanks for the explanation, i didn't realise it was changing it just for rendering

stiff quiver
#

@pine cape if we dont supply the server with a NetcodeConfig::private_key, will it assume that every connect token is ok?

pine cape
#

it uses 0 as private_key

stiff quiver
# pine cape it uses 0 as private_key

yea, so if i wanted to make a separate api that creates valid connect tokens, they would need to be generated with the same private key we supply to the config right?

pine cape
#

yes exactly, the backend server generating the tokens need to share the same private_key as the game servers

stiff quiver
#

alright awesome

stiff quiver
#

i just discovered how neat tmux is for running client and server watchers in parallel

pine cape
#

what do you mean by watchers?

stiff quiver
#

and i run it alongside cargo watch for the server

pine cape
#

like client/server processes? yes I always have tmux on 🙂

stiff quiver
#

very neat :P

bronze elm
#

also I think that was the problem I ran into earlier... I can't implement Linear for Position (xpbd struct), cuz you can't define a trait for a type outside of the current crate

pine cape
stiff quiver
#

here i mean

#

wrong link

pine cape
#

no xpbd keeps transform and Position in sync

stiff quiver
#

i think so at least

pine cape
#

it does, if you add Position, transform will get added

stiff quiver
#

ah okay

#

ah i get it now, position is basically globaltransform->translation

#

but eh

#

doesnt that mean the local pos would get lost

#

actually nvm if it is transform->translation it could just calc globaltransform

#

yea that makes more sense

bronze elm
#

is it possible to use VisualInterpolationPlugin on Position? If the physics is using FixedUpdate, the position is only sync'd to transform in the PhysicsSet::Sync set, so what I'm seeing is basically the player staying in the same position. I tried changing the order of UpdateVisualInterpolationState to run before PhysicsSet::Sync but maybe I'm thinking about this wrong

pine cape
#

yes you should be able to, as long as you specified an interpolation fn for Position

wintry dome
#

is it possible to use the VisualInterpolationPlugin on predicted entities? looks like it requires an interpolation_fn to be set on the protocol, but if i do that i think it'll mess with my predicted entity?

wintry dome
#

I'd like to see how many ticks of inputs my client is predicting/guessing for remote players. ie, i need to get the latest tick for which i've received inputs for a remote player. If i'm on tick 10, and i can see that i've got inputs for a remote player up to tick 8, I want to write a "2" next to the player. I don't think this is easily accessible at the mo.

#

i want to show some stats next to each player, then host a public server (with wasm client) for a trial, and then pull-request this spaceships example.

other things i'd like to add:

  • figure out how to correctly spawn remote players' bullets based on their inputs, instead of waiting on the server to do it.
  • automatically tune players' input delays based on latency
pine cape
# wintry dome is it possible to use the VisualInterpolationPlugin on predicted entities? looks...

Yes it's not very clear, but the VisualInterpolationPlugin should ONLY be used for predicted entities.
(Because those are the ones that are updated on FixedUpdate, and that need some visual interpolation at PostUpdate time)
Maybe I should move it in the prediction folder?
You can see an example here: https://github.com/cBournhonesque/lightyear/blob/main/examples/replication_groups/src/client.rs#L121

GitHub

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

wintry dome
#

ok, that's a useful example. feels like the term "interpolated" is a bit overloaded.. on the one hand, you have predicted entities (ahead of server) and interpolated entities (behind server), but you also need to do interpolation in the general sense of the word for other reasons

#

also the protocol uses .add_interpolation(Once | Simple) to mean simply copy components to the predicted entity without doing any interpolation

#

wait, perhaps i'm wrong on the last point. i don't need to add_interpolation unless it's for something that matters visually, if everything is predicted. so interpolation is a reasonable name in that case.

pine cape
#

I don't think you even need to call .add_interpolation here, you can just register an interpolation fn

wintry dome
pine cape
#

because I have an option to interpolate other players (if predict_all = false in the settings), whereas you're predicting everything

wintry dome
#

i see

#

with everything spawned on the server is it safe to put the channels as ChannelDirection::ServerToClient too?

#

xpbd example spawns player cubes locally, so presumably that's why bidirectional was needed there

pine cape
#

yes I think it should be fine; it's also cheaper because then you're not adding systems dedicated for client->server sending

wintry dome
#

ok

#

so in the protocol, .add_interpolation is the counterpoint to .add_prediction - you either get an Interpolated (snapshot interpolation) or Predicted (client predicts, rollbacks) entity as a result.

#

.add_interpolation_fn.. allows you to specify how the interp fn for .add_interpolation entities works? lerp/slerp/custom whatever

#

and is also used for VisualInterpolationPlugin ?

pine cape
#

yes exactly, the interpolation fn is used both by the Interpolated entity and by the VisualInterpolationPlugin, since it's just a general function that describes how interpolation works

wintry dome
#

got it, thanks

#

and the add_correction_fn is only used for predicted entities, to blend in rollback errors (outside of fixedupdate, for rendering)?

pine cape
#

I should probably update the docs, I guess it's not clear enough

wintry dome
#

neat. i like that it can change the pos/rot for me outside of fixedupdate just for rendering without the physics systems seeing it. simplifies a lot.

#

i'll do a docs pass once i have finished this example, i'll hopefully know what's going on by then 🙂

wintry dome
pine cape
#
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

wintry dome
#

yeah i think end_tick is what i want. i could change visiblity enough so consumers could access it, or perhaps write it to a new component? PredictionStatus { missing_tick_inputs: u16 } regardless of input type

pine cape
#

ah didn't notice it was private; yes feel free to change the visibility!

wintry dome
#

ok

bronze elm
pine cape
#

I think predicting remote players is pretty niche, it's mostly where you need very good collision support or interaction with other players.
For a rust-like game I would recommend either interpolating everyone, or predicting local player + interpolating remote players

stiff quiver
#

@pine cape did u read the new issues on gh?

#

they seem to stem from the same bug i think

#

cuz when u refresh netcode isnt made aware of the disconnect i think

#

and the server thus thinks the client id is still in use

#

Which causes all the weird bugs

#

Tho the server does seem to know that the client disconnected, it just doesnt propagate it to netcode or smth?

pine cape
#

it should be propagated to netcode now, but i'll have t ocheck

stiff quiver
stiff quiver
#

@pine cape can confirm that it still happens on main (latest commit)

#

idk how exactly to fix it tho, im not too familiar with netcode stuff

pine cape
#

I don't think it's related to netcode

stiff quiver
#

Yea doesnt seem so

#

Exactly 3 huh

pine cape
#

maybe 3 is a coincidence; like the time it takes to reach an internal timeout or something

stiff quiver
stiff quiver
#

@pine cape does lz4 work on wasm?

#

cuz zstd isnt wasm compatible if i recall correctly

#

also another thing that might make sense is using brotli since its already designed for small size payloads

pine cape
#

I think it does, but a problem is that sometimes the output size can be bigger than the max packet size, so i'll have to rethink where to put compression

stiff quiver
#

also websocket (and i think wt too) already have per-message-deflate so its prob not that bad if we dont have a web version for that

#

(tho this would be required to be present at the transport crate level)

#

which im not too sure about

stiff quiver
#

@pine cape if i have both interpolation and prediction (as per the xpbd physics example) enabled on an entity which entity should i actually render to have smooth frames? the interpolated, predicted or confirmed? cuz for some reason if i do Without<Confirmed> i still get some jittery rendering if the entities are moving pretty fast thonk

pine cape
#

you shouldn't have both interpolation and prediction enabled on an entity

#

it's either Interpolation if you don't do rollbacks, or Prediction (+ VisualInterpolation) if you do

stiff quiver
#

ah

#

ye ok that makes sense

#

ah ic now, the examples have it as a config option

#

thought they enabled both

pine cape
#

yes exactly

jaunty granite
#

How do you use entity mapping with messages? If I want to send a FireOn(some_entity) to the server, does lightyear handle making sure that some_entity references the right entity when it gets there?

wintry dome
jaunty granite
#

lightyear feels kinda like magic so far

wintry dome
jaunty granite
#

What about for getting a client's ClientId? There are a lot of examples and I haven't yet found one that does that

#

Might be an xy problem though, the idea is I want to know which entity the server spawned for me so I can display its stats on screen

wintry dome
#

it'll automatically get the Controlled component on your client i believe

jaunty granite
#

Ahhhhhhhh

#

Thanks!

wintry dome
#

here's a snippet of an example i'm working on where the server spawns your client once you connect:


/// When we receive other players (whether they are predicted or interpolated), we want to add the physics components
/// so that our predicted entities can predict collisions with them correctly
#[allow(clippy::type_complexity)]
fn handle_new_player(
    connection: Res<ClientConnection>,
    mut commands: Commands,
    mut player_query: Query<(Entity, Has<Controlled>), (Added<Predicted>, With<Player>)>,
) {
    for (entity, is_controlled) in player_query.iter_mut() {
        // is this our own entity?
        if is_controlled {
            info!("Own player replicated to us, adding inputmap {entity:?}");
            commands
                .entity(entity)
                .insert(InputMap::new([
                    (PlayerActions::Up, KeyCode::ArrowUp),
                    (PlayerActions::Down, KeyCode::ArrowDown),
                    (PlayerActions::Left, KeyCode::ArrowLeft),
                    (PlayerActions::Right, KeyCode::ArrowRight),
                    (PlayerActions::Fire, KeyCode::Space),
                ]))
                .insert(ActionState::<PlayerActions>::default());
        } else {
            info!("Remote player replicated to us: {entity:?}");
        }
        let client_id = connection.id();
        info!(?entity, ?client_id, "adding physics to predicted player");
        commands
            .entity(entity)
            .insert(PhysicsBundle::player_ship())
            .insert(ExternalForce::ZERO.with_persistence(false));
    }
}
#

Player is my own component, but Controlled is a lightyear feature. You need to check Added<Interpolated> instead of Added<Predicted> if you specify interpolation in the protocol instead of prediction

#

note the controlled_by: ControlledBy... bit

wintry dome
#

has anyone done any work on hosting servers + wasm clients? getting a connect token etc. i'd like to be able to push my code to a public demo site for testing.

stiff quiver
#

"Auth" or smth

wintry dome
#

oh thanks, i'd missed that

pine cape
#

And you want your game server to be the same machine as the website right?

wintry dome
wintry dome
#

i've added a child entity to my predicted player entity. the child has a Text2dBundle used for a label with the player name. it appears to be stutting though, i think because the position the player is rendered at doesn't match the transform (the transform which is inherited by the child label entity). it doesn't match because of the prediction correction and visual interpolation

pine cape
#

I see

#

the PostUpdate visual systems need to be order with transform propagation

#

will make an issue

wintry dome
#

in theory i should be able to write a system that takes the difference between my (corrected, visually interpolated) Position, and transform.translation, and apply the delta to the child label. but it's not working, possibly my ordering is wrong

#
        app.add_systems(
            PostUpdate,
            position_labels
                .after(InterpolationSet::Interpolate)
                .before(InterpolationSet::RestoreVisualInterpolation)
                .after(PredictionSet::VisualCorrection)
                .before(PredictionSet::RestoreVisualCorrection)
        );
#

i can detect the delta between position and translation, but making changes to the child transform doesn't seem to improve things

#

do you do the postupdate visual stuff before or after bevy::transform::TransformSystem::TransformPropagate at the mo?

pine cape
#

VisualCorrection is done before TransformPropagate already

#

VisualInterpolation has no ordering

#

i'll add it now

wintry dome
#

ok

pine cape
#

maybe you can try it before i merge?

wintry dome
#

will do

#

no change. should it even work? the ship is rendered using it's Position which is massaged by lightyear before transform propagate. the label is a child entity, rendered at its Transform.translation which is inherite from the parent entity

#

not sure it can work like that

pine cape
#

The ship is rendered using its PostUpdate position after all visual-interpolation / correction, so that's what we should be propagating

#

are you using Position instead of Transform directly?

#

the problem might then be when xpbd converts Position to Transform

#

or you could try using Transform directly

wintry dome
#

i'm rendering using Position for the ship. the label is a text bundle, which is rendered for me using its translation

#

am i even rendering after transform propagate ?

        app.add_systems(
            PostUpdate,
            (
                draw_predicted_entities,
                draw_confirmed_entities.run_if(is_server),
            )
                .chain()
                .after(InterpolationSet::Interpolate)
                .after(PredictionSet::VisualCorrection),
        );
pine cape
#

ah right, you might want to render after TransformPropagate

#

or just render in Last to be sure

wintry dome
#

i put my rendering in Last - so the correction and visualinterpolation stuff will have run, and propagated to transforms, so the child label should be correctly positioned, but it still seems stuttery. i'll add more logging and investigate.

#

perhaps lightyear should provide a set into which I put my renderer?

pine cape
#

yes maybe, but in any case you would have to render after TransformPropagate

#

and lightyear operates before that

wintry dome
#

ok

jaunty granite
#

I've got a simple system where clients send messages FireAt(Entity) to the server, then the server spawns bullets that hit the specified entity. I've set up entity mapping for FireAt but for some reason they're not mapping right for some of the clients. I made sure the latest code was running on the buggy clients. I'm new to lightyear so I assume this is a rookie mistake of some sort. Is this enough info to guess at what I'm doing wrong? I can provide any additional details needed

pine cape
#

The entity mapping only works on the receiver side, i.e. the client maintains a mapping from the local entity to the server entity. When you send FireAt(Entity) to the server, there is no entity mapping being done actually! (since the server is the sender, it doesn't have any mapping)

#

But sending FireAt(Entity) events from client to server seems strange, wouldn't the input just be Fire? and then the server can figure out where the client was firing based on the client entity's direction or something

jaunty granite
#

Hmm, honestly I have no idea what the best design is. The client selects a target and after a warm-up time, the player's ship fires a shot that hits after x time. There's no collision detection, I just roll at the 70% mark to see if the shot hit, then animate it missing/hitting based on the result

#

Seemed to me like the best way to do that would be through messages since the input isn't real time

#

So the shot is modelled as (ShotProgress, ShotSpeed, Damage), and every frame progress is incremented by speed, and when progress > 0.7 I either insert Missed or let it continue on

pine cape
#

It's also possible to map the client_entity to the server_entity, but you will have to do it manually right now.
You can do the mapping like this (on the client): connection_manager.replication_receiver.remote_entity_map.get_remote(local_entity)

jaunty granite
pine cape
#

Hm I would have to think about it more, I think what I suggested above is probably the simplest way to do it right now.

stiff quiver
#

@pine cape how difficult would it be to integrate leafwing abilities with lightyear? thonk

pine cape
#

I'm not too familiar with it, but I think you can already do it no? since the leafwing_inputs are replicated

stiff quiver
#

hmm alright

stiff quiver
#

The biggest issue would be to have the cooldown serverside

#

im guessing you could replicate it to the client and make a custom check on the server

#

Kinda annoying but sure

wintry dome
#

my server is sending InputMessages to clients containing other players' inputs. It's using the built-in InputChannel, but i just noticed that lightyear configures this channel as direction: ChannelDirection::ClientToServer (but it still sends)

pine cape
#

hm you're right, that's strange

pine cape
#

the Channel's direction is not used right now, only the direction that is set when the message is regsistered

wintry dome
#

i think it should be registered as bidirectional anyway, since servers can rebroadcast inputs

pine cape
#

you're right

#

I might just remove it, since the direction is set in the protocol directly

pine cape
#

@wintry dome I updated the way messages are sent over the network. Instead of having a global send_interval timer that decides how often packets are sent, packets are now sent every frame, but you can send a send_interval per Channel.
That means you can send the replication_send_interval to 10Hz, but send inputs (and in particular broadcast other players' inputs) every frame. This was a big missing piece for being able to do better prediction of other clients.

#

We were originally at 1) predict other players using their replicated ActionState for rollback. (the delay is send_interval + the server has to reach the inputs' tick to apply it to the entity)
Then we added 2) also send their inputs so that during rollback we might have some 'future' inputs to improve out prediction (the delay is send_interval)
And now we have 3) send the other players' inputs every frame, so that we have even more input data

wintry dome
#

i'm currently struggling to get the remote players input stuff working as i expect it to - if i set a nice high input delay, and minimal latency, i should receive movement inputs for remote players before i need them, and thus never have to rollback (in a game with just 2 players). i'm fairly sure i am receiving them before the tick they're needed on, but my client is still doing rollbacks.

wintry dome
#

i can see the action diff buffer being updated with future inputs for the remote player, before the tick they're needed on, but for some reason the remote player still finds empty inputs in their actionstate for the tick we were sent inputs for. is it possible future inputs are being truncated or reset as part of normal replication somehow? i need to dig intothe buffering/storing/fetching inputs code some more.

pine cape
wintry dome
#

ok

pine cape
#

Input handling in leafwing now works in FixedUpdate: https://github.com/Leafwing-Studios/leafwing-input-manager/pull/522 ! It's going to make things much easier. For example no need to worry about using just_pressed or pressed + consume in FixedUpdate, just_pressed should just work. @dull lion

GitHub

Context
Fixes #252
(see description of the issue in bevyengine/bevy#6183)
It would also fix cBournhonesque/lightyear#349
(I did a write-up here: https://hackmd.io/_TGuaUTnRBeuisvUMr0QoQ?both)
i.e. ...

wintry dome
pine cape
#

@wintry dome I fixed inputs: https://github.com/cBournhonesque/lightyear/pull/472
Overall handling inputs should be more simple now, no need to have weird rules like

action_state_target_override: OverrideTargetComponent::new(
    NetworkTarget::AllExceptSingle(id),
),

Also we update remote_player entities by using the received InputMessage directly instead of using the replicated ActionState. In general ActionState is not replicated at all anymore, we only rely on InputMessage which is sent every frame, so with a bit of input delay it's possible to completely avoid rollbacks.
(there's still some scarce rollbacks in the xpbd_physics example, not sure why)

GitHub

Using remote clients' inputs for improving rollback and prediction was somehow broken, I think the main reason was that we were relying on ActionState getting replicated to the client for this ...

wintry dome
wintry dome
#

working nicely, no rollbacks anymore 🏆 i think that's simplified the input handling too. also i'm able to spawn remote players' bullets based on inputs too, so remote player firing doesnt cause rollbacks either. i add my own hash to PreSpawnedPlayerObject based on the player's client_id and tick.

dull lion
#

I was going to try out the new just_pressed functionality but then realized that LWIM hasn't pushed the new version yet. oh well, gotta be patient I suppose!

#

really awesome seeing all the progress here on input handling though

wintry dome
#

@pine cape what state is your lightyear_backend repo in, could i use it to spin up a demo on edgegap if i registered an account?

pine cape
pine cape
pine cape
#

Edgegap works well for hosting the game server ; but the webserver part is lacking. It should do:

  1. tell edgegap we have a new connection request
  2. wait for edgegap to spin up a new container or give me an existing session, with a server_addr
  3. use the server_addr to generate a ConnectToken
  4. send the ConnectToken to the client so that the client can connect to the game server
wintry dome
wintry dome
wintry dome
pine cape
jade ember
#

Do I understand that correctly that i need to derive Serialize and Deserialize to be able to send things around?
I mean it makes sense, but I'm wondering if it's possible to provide my own serialize/deserialize functions?

mainly i want to rely on reflection for this and need access to the typeregistry and such... would that work?
before i deep dive and might miss stuff, thought i'd ask here first

pine cape
#

You're correct, the Serialize/Deserialize traits are necessary currently. I didn't provide custom ser/de because nobody asked for them, but I can add it for the next release if you want, should be pretty quick.
I'm not sure how easy it will be to give you access to the typeregistry though

pine cape
jaunty granite
#

Do you know if anyone's ever made a turn-based game with lightyear? I'm starting to get the feeling lightyear's "happy path" isn't ideal for things like turn-based, but I'm not super deep into it either

#

At the very least, I feel like the docs maybe have some gaps for workflows off the happy path, but really good otherwise

pine cape
jaunty granite
wintry dome
#

debugging a shooting bug, i discovered that seahash gives different values on wasm vs native, on my arm mac:

        let mut hasher = seahash::SeaHasher::new();
        player.client_id.hash(&mut hasher);
        weapon.last_fire_tick.hash(&mut hasher);
        let hash = hasher.finish();
        let prespawned = PreSpawnedPlayerObject { hash: Some(hash) };

i'm logging the client id and tick i use, i can see it's the same but produces a different hash. not good for the prespawned stuff 🫤

pine cape
#

hm i've tested the bullet_prespawn on wasm and it seems to work fine for me

#

I have a m1 mac as well

#

the code for seahash looks pretty deterministic after a cursory look

wintry dome
#

yeah it's supposed to be portable, i've probably done something daft. will double check it later, swapped in an xor for now.

wintry dome
#

if i register a component in my protocol as .add_prediction(ComponentSyncMode::None); does that mean that, should i add that component on the client (because it won't be replicated), that component will participate in rollback?

wintry dome
#

managed to get some weird behaviour by connecting newer clients to an older server instance. would be convenient to do something like this in the protocol setup:

        // reject any communication unless protocol versions are sufficiently compatible
        app.set_protocol_version("0.1", |other: String| other.starts_with("0.1"));

and have the server reject the connection if necessary

#

oh wait there's the netcode protocol ID for that I suppose

pine cape
pine cape
wintry dome
#

built my demo game server for linux, discovered in the process that the steamworks lib doesn't build on linux. i have some patches to submit that add some compile time checks to avoid referencing steam lib stuff if the cargo feature is deselected.

#

(needs chrome desktop for now) https://game.metabrew.com/

hosting the demo here at the mo. it's a server in germany, with 6 ticks of input delay hardcoded, so best experience if you're within ~80ms of the server i think. it's 70mb uncompresed wasm.. still got a few things to figure out with shooting and different latency conditions. and i sometimes get loads of rollbacks for no obvious reason.

pine cape
#

Awesome!!

wintry dome
#

is lightyear tracking how many ticks ahead of the server the client is simulating? or more specifically, can i tell how far ahead of the server needing it my inputs arrive? i'd like to be able to dynamically tune the input delay a bit on the clients.

pine cape
#

The client tick will be just far enough of the server so that the client inputs arrive at the correct time on the server

#

I'm going to make the input/prediction stuff a bit more configurable, I just haven't gotten around to it

#

which should cover what you want

wintry dome
#

cool, makes sense

pine cape
wintry dome
#

since playing around with wasm clients, i occasionally find them really laggy or behind somehow. possibly because the browser half goes to sleep when it loses focus for a while or something. no fun debugging browser things

#

i previously implemented what rocket league vid calls "upstream throttle", which worked pretty well when latency fluctuates. not tried dynamically messing with the latency with lightyear yet. i think it's just browser idosyncracies

pine cape
#

lightyear also runs on upstream throttle

#

the App.update() runs only once a second when the tab is in the background, currently

dull lion
#

on the topic of sparse component storage, it seems that the consensus is that they're not worth it because they slow down querying speed

pine cape
#

Yes, I switched most of them back to dense, I'm going to switch all

royal saddle
#

Hey, looking at the simple_box example I was wondering why the render code is in the shared.rs file. The comment at the top mentions

The rendering code is here because you might want to run the example in host-server mode, where the server also acts as a client.
but from what I can tell, the host-server mode also runs the client plugin, so would it actually matter if it was in shared.rs or in client.rs ?

wintry dome
#

see assets/settings.ron

royal saddle
#

but would you actually render any of the client stuff in that case on that server? If yes, why wouldn't you use the host-server or host-and-client setup that the example has ?

wintry dome
#

the server renders the Confirmed entity, but the clients would typically render the Predicted or Interpolated version

royal saddle
#

I see, so it's a live view of the world as seen from the server

wintry dome
#

the confirmed entity on the client only updates every (replication interval, examples default to 100ms iirc). so you render the interpolated or predicted one instead for a smooth view

royal saddle
#

Another question I have which is maybe more suited for the xpbd discussion, but I'll try it anyway. I modified the lightyear example with xpbd to use xpbd3d (it was using 2d). This would no longer allow the server to actually run panicking on the following:

Resource requested by bevy_xpbd_3d::plugins::collision::collider_backend::init_async_scene_colliders does not exist: bevy_asset::assets::Assets<bevy_render::mesh::mesh::Mesh>Resource requested by bevy_xpbd_3d::plugins::collision::collider_backend::init_async_colliders does not exist: bevy_asset::assets::Assets<bevy_render::mesh::mesh::Mesh>

I can make it to run if I add the AssetPlugin, RenderPlugin, WindowPlugin, ImagePlugin and ScenePlugin along with MinimalPlugins. Is this expected or am I doing something dumb here

pine cape
pine cape
royal saddle
#

alright, thanks!

pine cape
royal saddle
#

ah interesting, I'll check that out

pine cape
#

(it might be a little bit out of date)

royal saddle
#

do you by any chance know about an example with rapier ?

pine cape
#

I don't know of any, sorry

royal saddle
#

np :D

pine cape
wintry dome
#

neat, i'm still in 0.13 land for now, but will try it once i switch to 14

#

i'm about ready to PR my spaceships example i think

#

there are a few changes to lightyear, small things. my commit history is a mess though. one big squash commit when you merge should hide all the crimes though. hopefully not too annoying to review.

#

just gonna update my hosted wasm+server then i'll send the pr

pine cape
#

sounds good, thanks

wintry dome
#

argh, almost. i pushed wasm that conects to localhost. one sec

#

fixed

pine cape
#

nice thanks. So the future inputs are used only for rollbacks and for bullet spawning, but not for movement, right?

wintry dome
#

future inputs are used for movement, but future fire inputs are only used for bullet spawning not in rollback, ie the inputs arrived before the client needed them

#

in cases where fire inputs arrive late, the bullets are replicated nornally from the server resulting in a rollback

pine cape
#

Are they actually used for movement though? because the movement will only depend on actual replicated components. (they are used during rollback though)

wintry dome
#

the player_movement system will apply forces to remote players if inputs are available for that tick, rollback or otherwise

#

so yes, they are used for movement. if you increase latency/decrease input delay, (so you get a negative # buffered inputs in the label, like [-1]) then you can see the twitchiness where rollback compensates for mispreditions eg when rotating. with +1 buffered inputs, that twitchiness goes away

#

with +1 buffered remote inputs you can see remote players move without any rollbacks (although sometimes you get rollbacks randomly, not sure why yet)

#

(cargo test passes locally, not sure why it fails in the github runner for my pr)

pine cape
#

What does a negative # of buffered inputs represent?
Ok I think I understand. The future client inputs are used locally to have a more precise simulation; the results should be similar to what we receive afterwards from the server (which has access to all inputs)

wintry dome
#

yes, inputs for a remote player's tick N can arrive before your client simulates N. if so, we apply them to movement and bullet spawning on N for better prediction

#

[-3] means you are simulating tick 10, but only have inputs up to tick 7

#

aka the window of bullshit where you guess which inputs to use to predict remote players' actions

#

and [1] means you are simulating tick 10, and have inputs up to tick 11, so your prediction should be perfect and not result in rollbacks

#

if you can connect people to servers with a reasonably small ping, like 50-75ms, with a sensible amount of input delay, the simulation should be pretty good 🤞

pine cape
#

the game works pretty well on wasm 😄

#

assuming that was you

wintry dome
#

yeah that was me

#

if i leave the wasm one running for a while, it gets into a weird state so i had to reload, but mostly works well

#

gtg for a bit

pine cape
pine cape
#

I've been reading your example code, it's really nice! The comments for the bullet firing (prespawned on the client if we have the input, otherwise we get it in rollback from server replication) are very clear
I noticed that the Collisions code is commented out, did you encounter some issues there?

wintry dome
#

what happens if my client is simulating a tick for which there are no inputs available for a remote player, do I just see a default ActionState (IE nothing pressed) for their entity?

#

do I need to check InputBuffer to know 🤔

#

if I'm yet to receive the inputs I want to use the most recently received input as a best guess, but I don't think that's happening at the mo

#

(I do that to render the engine exhaust in the renderer)

pine cape
#

I think the previous behaviour was that it was using the most recent input as a best guess, since we were relying on ActionState getting replicated. (ActionState would remain unchanged if we miss an input, which is basically equivalent to reusing the most recently received input)

Now that we switched to using the InputBuffer directly, that's not the case anymore, and I should probably let the user provide a strategy for how to handle missing inputs (where re-using the recently received inputs would be the default strategy)

wintry dome
#

I would like to use most recent input for movement, but ignore fire inputs unless they are current

wintry dome
#

i think this might do what i want, not tested. sometimes you might want to decay various forces if the inputs are predicted, and decay more depending on how many ticks of missing inputs you have. so whatever solution needs to handle that somehow. if i could call a fn on ActionState and get a 0 if the inputs are current, and a 3 if they are a clone of the last known input which is 3 ticks ago, that would be simple enough to integrate into my system.

#

i'd like to address this in the demo somehow, or at least leave a comment explaining the situation. quite important to reuse stale inputs, at least for a few ticks, to reduce rollbacks.

pine cape
#

Yes I think this works, but I'm not sure I understand the first if condition. Shouldn't you check directly that there is an input for the current tick? Or is it that you know in those cases that I have read the value from the InputBuffer and applied it to the ActionState already?

pine cape
#

I guess it works, but it would probably be clearer to just check if the current tick is present in the InputBuffer

wintry dome
#

You're right, it looks weird.. I think I did that because get() isnt public

pine cape
#

Btw what did you use to host your demo? (As in which company)

wintry dome
#

i already had a linux server from hetzner, a cheap server company in germany. running my blog and a few other sites. i cloned and built it on the server, but had to comment out the steam feature in examples/common, since steamworks doesnt build on linux

#

oh you merged my spaceships branch thanks 🙂
my commit history was kinda messy so it has polluted your commit timeline a bit 😬

#

the Right Way (tm) nowadays is probably to build a docker image of the server.. i started looking at edgegap, which looks very cool but will also require another separate server to coordinate things i think, so not the simplest solution (one i do want to explore though)

pine cape
#

I thought the commits would get merged into one

wintry dome
#

yeah maybe there's a check box when you clikc merge to do a squash, it often does

pine cape
wintry dome
#

are you interested in hosting the lightyear examples, or thinking more about shipping something else? i could do a bit of refactoring and get dockerfiles for servers for the examples

wintry dome
pine cape
#

I actually used to host them but stopped

#

But I should put them up again

#

Let me check if I can find my edgegap dockerfiles for you

wintry dome
#

i have another repo where i refactored spaceships into client/server with different config, to make it easier to build server docker containers. was planning to try that on edgegap, but somehow despite it being more or less the same code, i introduced a death spiral bug somehow. after a while frame time balloons and it lags out. still got to investigate that

pine cape
#
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

#

(since they require the images to be for amd and I have a mac m1)
With this I was able to have edgegap deploy a gameserver, and I could connect my clients to it

pine cape
wintry dome
#

yeah it's the code from the example, happening with like 10 entities.. but not on the example, need to triple check how i have it configured.

wintry dome
#

probably worth building one docker image containing all the examples, and just launching with a different entrypoint depending on what example you want

wintry dome
#

i've been poking around in the input/leafwing code. hard to distinguish between missing inputs and a default actionstate due to no keys pressed atm. both on client (remote inputs arrive late) and server (player inputs arrive too late). how do you feel about games changing from using ActionState to something like PlayerInputs{ action: Option<ActionState>, stale_ticks: u16} so action can be None, or i could write the most recently received action and mark it as stale by stale_ticks in the case of remote players (ie, you're reusing an input from 2 ticks ago because you didn't get more recent inputs). in the latter case, the game's system could notice it was stale (stale_ticks >0) and decay inputs or whatever they want.

#

willing to have a go at refactoring that if you think it's a sane approach

pine cape
#

Hm, doesn't the approach of checking if there's data for the current tick in the InputBuffer work? I could just provide a helper function for that?

I quite like avoiding any indirection and letting users use ActionState directly

#

I updated the logic to not update the ActionState from the InputBuffer if the input is missing (which basically means that the latest received input is used)

wintry dome
#

i think it's important for games to know the input is missing

#

at the mo game systems can use input_buffer.get(tick) where tick is tick_manager.tick() or the rollback tick, but then you want a helper in case the remote inputs haven't arrived, so you fetch the most recent stale inputs and report how many ticks old they are somehow

#

writing ActionState as a component is already unnecessary technically, since you can use input buffer directly and it gives you more info (like if the inputs are missing)

#

but if the ActionState component told you about missing inputs/how stale they are, that would be an upgrade. seems a shame to even expose input buffer to game systems since we're already writing from it to ActionState

#

i like the ergonomics of having one component with that tick's inputs in, rather than having to query input buffer. if i always have to check input buffer to see if the ActionState inputs are default because they were missing, or stale, it seems pointless to even write to ActionState, might as well just grab it from input buffer in my game system

wintry dome
#

🤔 could write another component alongside actionstate that records if the input was missing for that tick, or if it's a stale input (and how many ticks old it is). easily ignored by people that don't care then.

royal saddle
#

I'm super confused 😅 I'm slowly adapting things from the xpbd/leafwing example
when I remove these lines from my protocol.rs:

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Hash, Reflect, Actionlike)]
pub enum AdminActions {
    SendMessage,
    Reset,
}

// This inside the ProtocolPlugin.build:
app.add_plugins(LeafwingInputPlugin::<AdminActions>::default());

I suddenly get the following error on the server when I try to spawn an entity from the client:
ERROR lightyear::shared::replication::receive: could not write the component to the entity: unknown component kind

AdminActions does not have a single appearance in the codebase (with the exception of some commented out code), so I'm somewhat confused on how this affects the protocol in such a drastic way.

#

ah of course while finally posting something after mulling it over for a while, I have an idea of what it might be.
I added a app.add_plugins(LeafwingInputPlugin::<InitialAction>::default()); to just the ClientPlugin, as I wanted to capture some input purely client side that should not be replicated. Is that registration somehow messing up the protocol ?

#

ok yeah changing that LeafwingInputPlugin for InputManagerPlugin which is the non-lightyear variant, fixes it.
May it be of use to someone else looking for it in the future 😅

pine cape
#

I'm not sure I fully understood, but yes adding the LeafwingInputPlugin registers some types to the ComponentProtocol!

royal saddle
#

basically because I added it only on the client and not in the server, the protocol was no longer "in sync"

royal saddle
#

I have an entity created on a client that is getting replicated to all other clients. The collider I would like to add to this replicated entity on all clients should be offset from the base transform of the entity, so I use something like:

commands
  .spawn(player_bundle) // Contains client::Replicate
  .with_children(|parent| {
      parent.spawn((
          Transform::from_translation(Vec3::new(0.0, 2., 0.)),
          Collider::cuboid(3., 1., 3.),
      ));
  });

If I don't add this with_children section to my server code (or to the Added<Predicted> code on the other clients), the collider is only created on the initiating client, which makes sense as neither Transform nor Collider are replicated.

If I add a component that is getting replicated (e.g. Name in my protocol), it however also does not appear, even though client::Replicate::default() has recursive: true for its ReplicateHierarchy component.

If I manually add client::Replicate::default() and ParentSync::default() to the child entity, things become more confusing.
With the following code on the client:

commands
  .spawn(player_bundle) // Contains client::Replicate
  .with_children(|parent| {
      parent.spawn((
          Transform::from_translation(Vec3::new(0.0, 2., 0.)),
          Name::new("Child"),
          client::Replicate::default(),
          ParentSync::default(),
      ));
  });

The server now sees a child entity on the replicated entity with a Name component. Nice!
The initiating client however, now sees:

  • The (pre)predicted entity it made with a child entity containing all of the components defined
  • The confirmed entity from the server with a child entity containing the Name component
  • a separate top level entity with the Name component
#

So my questions are:

  1. Why does the child component not replicate without the explicit ParentSync&Replicate even though the default hierarchy has recursive:true?
  2. What's going on causing the latter example to spawn an extra top-level entity for the Name component?
#

hm ok for (2) If I explicitly say recursive: false in the server's Replicate, the extra top-level component is not created on the client.
So it feels to me like the server's hierarchy settings do work properly, but the client's don't ?

pine cape
#

Hm thanks for the report, it looks like something is broken with hierarchy propagation

#

In general in my unit tests I mostly check things in the server-to-client direction

royal saddle
#

ah ok thanks for the response; I'm very new to the ecosystem so I wasn't sure if I was overlooking a certain aspect

pine cape
#

Maybe you're not clear on one thing though: when you want to replicate from the client to other clients you need to first replicate from client to server, and then in a second step replicate from the server to other clients. (You cannot replicate directly to other clients)

royal saddle
#

yes I'm aware, that was not well explained mb

pine cape
#

Are you doing PrePrediction? Does the server replicate back to the original client and then gets authority on the entity?

royal saddle
#

yes it's using preprediction indeed

pine cape
#

It sounds like the issue is the combination of PrePrediction with hierarchy, the PrePredicted component isn't added on the child, which messes things up

#

Maybe try adding it manually for now

royal saddle
#

to be clear, the player bundle adds PrePredicted::default() and client::Replicate { group: REPLICATION_GROUP, ..default() }

royal saddle
pine cape
#

Otherwise I would appreciate it if you could create an issue 🙂 (with the server and client code used for replication)

royal saddle
#

I'll make one shortly!

wintry dome
#

what's the best way to convert a ClientId to that player's controlled entity, on client and server?

pine cape
#

On client, you have the Controlled component for that client's controlled entities. If you want to know the entities controlled by other players you need to do it yourself

#

I want to have a component like Controlled<ClientId> maybe, but it would only be possible with dynamic systems

#

For server, there is one entity representing each client. This entity will have the ControlledEntities components

#

You can get the entity from the server's ConnectionManager

wintry dome
#

thanks, i found the controlledentities thing, wasn't sure if there was a built in map for the client. i'll probably maintain a map as players join and leave on the client.

harsh crescent
#

Having trouble with HostServer. I've got a HostServer (with a local client) and a NetCode client all connected together.

  • When I send a message from the netcode client, both that netcode client and the server see the message (not the local client).
  • When I send a message from the server, only the netcode client sees the message (not the server or the local client).
  • When I send a message from the local client, nothing happens

I am pretty sure the local client is actually running as I see a ConnectEvent when it starts up. Also, the client entity that represents the local client is missing the Name component, but it has the ControlledEntities component. The NetCode client entity looks normal as far as I can tell.

Am I doing something wrong, or is this how it's supposed to work?

pine cape
royal saddle
#

ah super thanks!

#

strange that the issue didnt autoclose upon merge of the pr

pine cape
# harsh crescent Having trouble with HostServer. I've got a HostServer (with a local client) and ...

When I send a message from the netcode client, both that netcode client and the server see the message (not the local client).
That's strange, shouldn't only the receiver (i.e. the server) see the message, and not the netcode client? If you want the local client to see the message, you need to explicitly transmit the message from the server to the local client. Or are you using NetworkTarget::All
When I send a message from the server, only the netcode client sees the message (not the server or the local client).
That is the expected behaviour
When I send a message from the local client, nothing happens
That's weird as well.

Could you create an issue with your example code?

pine cape
#

Does anyone have thoughts about me removing entirely the native input handling (and only keep the leafwing)?
Pros:

  • input delay is currently only supported with leafwing, and is harder to implement with native
  • adds a maintenance burden to support both at the same time (I also haven't added remote player inputs to native)
    Cons:
  • forces users of the library to use leafwing-input-manager. Although I believe that the long-term plans is for leafwing to be upstreamed into bevy eventually
dull lion
#

tbh anyone thinking about having networked prediction is probably using leafwing

wintry dome
#

i think that would make sense for now, i have been reading input related code, and native is missing features compared to leafwing like you said. and the logic for leafwing regarding input handling is still in flux. always possible to redo it later once the leafwing side matures

royal saddle
#

The main branch has a xpbd example which has a replicate_inputs system in the server. If I copy this over along with the relevant app.add_systems I get an error about the resource not existing when I t ry to run the server. I noticed this function does not exist on the 15.1 branch (in the leafwing_inputs example as there is no xpbd example on that tag). Is this due to something that changed on the main branch ?

royal saddle
#

the reason I ask is because I'm trying to figure out why movement is not being predicted on remote clients.
I can see the Confirmed entity having ActionState<PlayerActions>, but the Predicted entity does not

pine cape
royal saddle
#

I see

#

the main branch is still on bevy 13 ?

pine cape
#

One of the main improvements of the new release is precisely being able to do remote player prediction correctly, which is what the spaceships and xpbd_physics example do

pine cape
royal saddle
#

when you say new release, you mean the upcoming one ?

pine cape
#

yes

royal saddle
#

alright I might just experiment with using the main branch as a dependency for a bit then and see if that resolves my current issue :D

pine cape
#

sg, hopefully it works!

#

Actually I don't understand in the examples how ActionState gets added on the Predicted entities for remote clients, since ActionState only has ClientToServer replication: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/shared/input/leafwing.rs#L45
(that's a recent change I made).
It seems to me that you would need to add the ActionState component yourself on the predicted remote player entities.

(Or I could update ActionState to be Bidirectional, with also ComponentSyncMode::Once)

GitHub

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

royal saddle
royal saddle
#

when I add it manually it fixes some things yes

royal saddle
#

ok got it working with the main branch!

pine cape
#

awesome

stiff quiver
#

@pine cape how would i go about having the client send its mouse position in the inputs (translated to world ofc)? i dont really get how the leafwing examples work thonk~1

pine cape
#

Hm you might have to set the world coordinates position directly in the ActionState

stiff quiver
#

uh action state* ig they call it

#

ah i guess just a vec2

pine cape
stiff quiver
#

i guess for anything else other than mouse pos i'd have to use messages then

pine cape
#

What other things to you have in mind?

stiff quiver
#

just camera rotation prob

pine cape
#

The native inputs can store anything, I guess that's an argument for keeping them

stiff quiver
#

mhh

#

maybe a way to directly influence the input buffer would be neat too

#

although prob not needed in most cases

pine cape
#

influence?

stiff quiver
#

like uh, basically write inputs without depending on native / leafwing

pine cape
#

well that's native basically. Native is just a raw input buffer provided by lightyear

stiff quiver
#

ah okay

#

well u should keep native either way imo, until leafwing eventually gets upstreamed ofc

#

but thats just future talk, rn it doesnt make sense to force the user into using leafwing imo

pine cape
#

it's just that a lot of things are broken in native I think (input delay, remote player inputs. Probably pre-prediction as well)

stiff quiver
#

its also a bit annoying that its essentially arbitrarily sized as long as its serializable anything goes, thats my understanding at least

#

tho i feel like for some that tradeoff might be worth

royal saddle
#

talking about the mouse, that was my next thing to add to my small test setup.
Currently wasd are sent using leafwing and both the server and predicted entities run some movement code based on the ActionState in FixedUpdate.
If I want to add mouse movement to rotate the entity around the Y axis (first person camera). How should I go about adding that?
The first person example in the tnua repository runs a system to reposition the camera on PostUpdate and stores a forward Vec3 that is used in the FixedUpdate movement system. If I were to do that I would only need to sync that Vec3 to the server and not all mouse input? does that sound about right? 😅

pine cape
#

Do you have the link to the tnua example? I lost it aha

royal saddle
#

the movement system is big, but the start is the essential part I'm currently interested in, which is just walking + camera

#

though it's using native input instead of leafwing

pine cape
#

the tnua demos are really cool, hadn't seen them before

#

I guess what I would do is not replicate the camera to the server, since it's mostly a render-related thing.
I would only replicate the character's rotation as a Vec2 (DualAxisInput) via leafwing inputs

#

i.e. on the client, have a system that takes the mouse input, calculate the player's rotation around the Y axis, set it on the entity AND on the ActionState, send that to the server.
On the server, use the ActionState's rotation to set the rotation of the player entity.

royal saddle
#

and in which schedule would you handle that ?

pine cape
#

In FixedUpdate

wintry dome
#

i PRed adding missing input prediction to the spaceships example, and added bullet collisions. (updated https://game.metabrew.com too, fixed 4 ticks input delay atm)
still get occasional bouts of rollbacks that i can't explain in some situations, but mostly behaving. just need bevy 0.14 to drop now, along with the fixes in the lightyear 0.16 branch.

silent patrol
#

Hi. I've probably a stupid question: why does WebTransportClient require client_addr? How is it used? Do clients open a listening socket on their end too?

wintry dome
#

with client port being 0 too, so it's assigned automatically

silent patrol
#

but why does a client need a bound socket?

wintry dome
#

it's not to listen, it's the interface and port to bind the udp socket to. normally you wouldn't bother specifying, since the OS's ephemeral port allocator assigns you a port, and the interface depends on the remote address. like if you want to connect to server on localhost, your socket will also bind on localhost. if you want to connect to an internet ip, your ip will be your LAN (if nat/gateway) or public ip

#

eg, the server might be set to listen on 127.0.0.1:5000 and the client would end up binding its socked to 127.0.0.1:<whatever port the kernel gives you>

#

(well it kind of is to listen, just not in the tcp sense.. the server sends you packets addressed back to 127.0.0.1:<whatever port the kernel gave you>)

silent patrol
#

oh, ok, thank you. After getting used to TCP sockets API, I forgot that sockets get bound under the hood anyway. And for UDP we do need to bind client sockets as well 👍

wintry dome
#

one obscure reason to manually assign the client's local port: you can use that in networking rules to manipulate latency etc at the kernel level, using iptables/dummynet/whatever utils windows has.

silent patrol
#

that's a nice details, thanks

stiff quiver
#

its not needed on the web

stiff quiver
#

@pine cape random idea, would it be possible to replicate states?

#

i feel like it prob isnt because it lives in the app basically

#

but idrk

pine cape
#

It's possible, states are basically similar to a resource, so we just need some code that wraps the state in a resource to replicate it

royal saddle
# pine cape i.e. on the client, have a system that takes the mouse input, calculate the play...

To come back to this; I've added the following system to the client:

fn camera_movement(
    mut rotation_query: Query<&mut Rotation, With<WheelBoy>>,
    camera_action_query: Query<&ActionState<CameraAction>>,
) {
    let camera_action_state = camera_action_query.single();
    let mut rotation = rotation_query.single_mut();

    if let Some(pair) = camera_action_state.clamped_axis_pair(&CameraAction::Pan) {
        rotation.0 = rotation.0.mul_quat(Quat::from_rotation_y(-0.1 * pair.x()));
    }
}

You mentioned also setting it on the ActionState, and that confuses me slightly as my system already reads from the actionstate.
Should I just ensure that the same actionstate is replicated (i.e. LeafwingInputPlugin::<CameraAction>) and basically run the same system again, but for a specific player?

#

Also Rotation is already being replicated bidirectionally, so do I actually need to send the actionstate over ?

#

though that alone doesn't give the desired effect :D so I'm missing something there

pine cape
#

What does your CameraAction contain? A vec2?

royal saddle
#

yeah, DualAxis::mouse_motion output to be precise

#

but it's a vec2

#

the above system does a decent job in just non-networked setting

pine cape
#

So basically the mouse position w.r.t to the client's window?

royal saddle
#

not the position

#

a motion vector, the direction you moved the mouse on the monitor plane

#

Currently I'm just interested in rotating the shape around the 3D Y axis based on the mouse's 2D X axis to get a feel on how to sync this info

#

so that works, but now I need to get it to the server

pine cape
#

i.e. the character turning to the left or to the right, like in the tnua demo?

royal saddle
#

without my local rotation being reset ? I'm not exactly sure what's happening, but I quickly jitter

#

yeah I suppose so

#

in place rotation around the Y axis

pine cape
#

I'm not sure I fully understand how your CameraAction motion vector works; but is the CameraAction enough for the server to also compute how the client's entity should move? That's the key part.

royal saddle
#

Yes, it should be, the same Rotation should be aplied on the player that triggered the action

#

so just to get a better picture of the networking. Rotation is in my protocol as ChannelDirection::Bidirectional & ComponentSyncMode::Full.
Does me changing the rotation in that client system above, do anything network wise as a result ?

#

(the client shape is pre-predicted)

pine cape
#

No; I'm a bit busy now but will come back in 30min

royal saddle
#

oh np take your time

pine cape
#

So, replicating properties such as Rotation from the client to server is not really the right approach; instead you want to send the client inputs to the server and have both the client and server update the world based on the inputs. This is key to get prediction working.
Also inputs are replicated more efficiently than state such as components.

#

But to answer your question, you would need to add the client::Replicate bundle to your entity for the rotation to be replicated from client to server. (but it's not what you want to do)

#

So the general flow is instead:
CLIENT

  • you move your mouse.
  • in PreUpdate, your ActionState is updated based on the mouse movement.
  • in FixedPreUpdate, the ActionState for the current tick N is stored in the InputBuffer
  • in FixedUpdate, you update your character's movement/rotation based on the ActionState
  • in PostUpdate, an InputMessage is sent to the server containing the last few ActionStates from the last few frames (created by reading the InputBuffer)

SERVER

  • the server receives the InputMessage containing the ActionState for tick N when it itself is simulating tick N-delta, and stores it in an internal buffer until it's ready to be used
  • in FixedUpdate when the server is simulating tick N, it pops the ActionState for the client from its internal buffer and applies it. Then you apply the same logic to update the character's movement/rotation
#

If your ActionState contains enough information for the server to update the movement of the character, then that's perfect, you're done!
It might be the case, if CameraAction is some kind of direction vector relative to your character.
I was thinking that you were going to need to somehow convert the mouse position to 3D world coordinates, which would have been problematic since leafwing-inputs doensn't handle transmitting vec3 data currently

royal saddle
#

ah ok that's super clear thanks

pine cape
#

So basically, for you there's nothing to do apart from making sure that you add in FixedUpdate a system that takes the ActionState and moves the character based on this. (and this system should run both in the server and the client)

royal saddle
#

should I just remove it, or what is a good usecase of it

pine cape
#

That's probably not what you want. You want the server to have authority over the entity. i.e. the server should be simulating the entity (by applying the client's inputs/ActionState) and then replicating the entity's new state to the clients

royal saddle
pine cape
#

Ah I see. That's fine, it's just for PrePrediction.

  1. The entity is spawned on the client in the predicted timeline
  2. client::Replicate is added to the entity, so it gets replicated to the server. client::Replicate is removed from the entity right after the first message is sent, because the entity is PrePredicted (that's done internally by lightyear)
  3. the server receives the entity and spawns it in the server world.
  4. You add server::Replicate to the entity on the server; the server is now simulating the entity and has authority over it
royal saddle
#

aha

#

ok thanks

pine cape
#

yes the pre-prediction logic is a bit confusing maybe

royal saddle
#

I'm slowly getting the hang of this :D

#

well it doesn't help that I'm completely new to this field

pine cape
#

Maybe to start you can get rid of pre-prediction, it's not really important. Just spawn an entity on the server directly when the client connects, and replicate them to clients

royal saddle
#

and spawn the shapes on the server instead

#

yeah that probably makes more sense, I just happened to start from that example and didn't want to veer too much away while I slowly experiment with things :D

pine cape
royal saddle
#

yeah I definitely see the use for it

pine cape
#
  • it makes things confusing if you don't have a working example of prediction/replication. Maybe i'll remove it from most examples and put it in a dedicated separate example
royal saddle
#

might be a good idea :D

pine cape
#

Another thing, the ActionState is written in the InputBuffer in this SystemSet: https://github.com/cBournhonesque/lightyear/blob/58d1f61f500797b9f2da535a47ebb3b3cd891802/lightyear/src/client/input/leafwing.rs#L298-L298 which runs in FixedPreUpdate

So any modifications you do to the ActionState (for example computing the CameraAction from the raw mouse data) have to happen before that. Either in PreUpdate, or in FixedPreUpdate but before that SystemSet

GitHub

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

royal saddle
#

but that's stuff that leafwing handles itself no ?
I don't populate the ActionState, I only consume it

pine cape
#

Maybe; is CameraActions updated directly from your mouse motion by leafwing?

royal saddle
#

yeah

#

I haven't looked yet how I would add custom stuff to the actionstate, as for now the key/mouse inputs themselve seemed fine for my usecase

pine cape
royal saddle
#
add_plugins(LeafwingInputPlugin::<CameraAction>::default()) // on client & server

// on the player entity client-side
commands.insert(InputManagerBundle::<CameraAction> {
    action_state: ActionState::default(),
    input_map: InputMap::new([(CameraAction::Pan, DualAxis::mouse_motion())]),
})
#

thing is, you're looking at the mouse position example

#

as I'm interested in the x/y movement of the mouse, not the actual position of the mouse

pine cape
#

It's just not clear what mouse_motion represents; just how much the mouse moved since the last frame?
but yeah you just need something that lets you accurately move the player's character from the server

#

glad to hear that it's working! do you have a video/demo to share? 🙂

fading jasper
#

does lightyear have any special handling for snapshot interpolation with client authority? I tried the client_replication example but the movement didn't look very smooth.

pine cape
#

You mean for the entity that you're simulating locally? You shouldn't need interpolation since you're simulating it yourself. Which movement didn't look smooth in the example?

fading jasper
#

I mean the movement of other players

pine cape
#

There is already interpolation added like so: https://github.com/cBournhonesque/lightyear/blob/main/examples/client_replication/src/server.rs#L127
Maybe it's choppy because you set server_send_interval to 0?
See this issue: https://github.com/cBournhonesque/lightyear/issues/423

GitHub

Problem If you set the tick rate above 60Hz, interpolated entities using the default linear interpolation will start to look very laggy, teleporting and freezing in place. How to reproduce In the c...

GitHub

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

#

It looks very smooth for me with the default settings used in the example

#

(you also need to make sure that you're rendering the Interpolated entity, not the Confirmed one)

fading jasper
#

I didn't make any changes, I just ran the example

pine cape
#

On main? I don't know, it's smooth for me. Is it also unsmooth for anyone else?

fading jasper
#

on main, yes. tomorrow I'll send a video of what it looks like.

stiff quiver
#

Has anyone ever tested if interpolation works if we are running a local server?

pine cape
#

You mean in host-server mode? there's no need to run interpolation at all, since the client world is the server world, so you 'receive' entity updates instantly

stiff quiver
#

So mocking interpolation would be neat i think?

#

same for prediction ofc

pine cape
#

I'm not sure I understand; in host-server mode you have the Client/Server plugins running in the same World, so the local client can see everything that the server sees. You can still run the exact same rendering code

#

In fact of the examples in lightyear can run in host-server mode

stiff quiver
#

so if you have logic that only renders interpolated entities then there would be nothing to render

pine cape
#

You don't need interpolation because you are the server. Your world is simulating every entity every frame, there's nothing to interpolate.

#

We still add the Predicted/Interpolated components on entities, even though the PredictionPlugin and InterpolationPlugin don't run

#

so that queries with With<Predicted> and With<Interpolated> still work on the client

stiff quiver
#

Oh ok neat

pine cape
#

that's how the examples manage to still work with 0 code change, even in host-server mode

fading jasper
#

I feel like this looks smoother than when I did it yesterday, but you can still see sometime the cursor teleports/hitches but maybe that's to be expected.

#

how do client owned objects get replicated exactly? is the server interpolating the snapshots it receives and then the server send snapshots of it's interpolated position to the other clients?

pine cape
#

Hm this looks good to me! I think it's similar to what I observe locally

pine cape
fading jasper
#

lightyear doesn't have client authority then?

pine cape
#

Ah, you're asking for entities that are directly replicated from client->server. The server doesn't have any interpolation for them

fading jasper
#

yes, I thought that's what this example was about

#

I'm just trying to understand how lightyear implement snapshot interpolation for client authorative objects

pine cape
#

Hm i see some teleports in your video actually, i see what you mean. Well currently there is just interpolation on the client side for entities received from the client, there is no interpolation on the server. I'm not sure we would want any interpolation on the server

fading jasper
#

so when the server receives a snapshot from the client, does it immediately forward it to the other clients or does it do it based on the server's tickrate? also, does it use a timestamp/tick on the snapshots from the client or from the server?

pine cape
#

When the server receives an update from the client, it applies it to its local entity immediately. Then the changes to that entity are forwarded to other clients in an independent system (and based on the server's replication rate).

When the other clients receive the update, they will use the server's tick.

Client C1 updates entity E1 at tick 5.
At tick 7, it gathers all replication updates. It sends a message to server with (E1, tick 7).
Server receives the message when it's at tick 6. (client runs ahead of server)
It waits until it's at tick 7 to apply the update (I might have to revisit this, maybe it's not necessary?)
At tick 10, server gathers all replication updates. It sends a message to client C2 with (E1, tick 10).
Client C2 receives the message (E1, tick 10) when it's at tick 14, and runs interpolation based on the server ticks.
(for example it's interpolation tick is at 8, so it interpolates between the messages it received for tick 6 and tick 10)

fading jasper
#

ok, so the client and server ticks are synced up then? also, is it not an issue that the other clients receive a different tick? I feel like that could cause stutters

#

for example, I had an implementation of snapshot interpolation where the client would send the server a snapshot, and every tick the server would replicate the last snapshot it received at the server's tick. this caused a lot stutters because the timestamp wasn't really consistent with the position it was sending

pine cape
#

A client's tick is synced to the server so that client_tick = server_tick + RTT/2
Basically the other clients just receive a replication from the current state of the server world. It's just that in this case the server world is updated quite infrequently / not smoothly

#

I don't think this should cause stutters. I guess there could be stutters because:

  • client updates are replicated every 100ms (+ packet loss/jitter)
  • server updates are replicated every 100ms (+ packet loss/jitter)
  • clients run interpolation considering that they receive updates every 100ms, but actually it could be every 200ms if they get unlucky (the server receives the change right after it already sent its own replication messages). So if you increase the interpolation_delay a bit, it should be much smoother
fading jasper
#

I'm more worried about the stutter caused by the ticks not matching the position. I'll try to come up with an example to make this more clear

#

It seems like your setup is pretty much what I was doing when I had the stutters

pine cape
#

No i see what you mean. (I think)
You're saying that on C1 the ball moves from 0 to 100 in ticks 0 to 5.
But on the server the ball moves from 0 to 100 in ticks 3 to 15 (for instance).
Then on client C2, when we run interpolation, we use the server ticks (3 to 15) for interpolation instead of the original ticks on C1

fading jasper
#

yeah essentially, and then you just get an inconsistentency over time on which ticks align with which positions and that cause stutters

pine cape
#

So originally the movement should be done in 5 ticks, but now it's done in 12 ticks which can make it weird

fading jasper
#

I'm working on snapshot interpolation right now where the server instantly forwards snapshots and sends them with the client's tick. that requires me to have a separate interpolation_time/interpolation_delay for each client tho

#

but this looks much smoother than my previous approach, I'm just seeing if there are alternatives at the moment

pine cape
fading jasper
#

right

#

I don't have synced ticks at all in my system

pine cape
#

I think I would need to create an example that showcases this kind of problem more clearly. For example a ball that moves at fixed intervals (teleports by 1cm every 5 ticks or something). For the server->client case it should look perfectly smooth with interpolation, but for client->client forwarding it might not look smooth at all

fading jasper
#

I'll try to experiment more with lightyear to see if it even has this stutter issue, I think mouse movement is probably not a good test for my usecase

royal saddle
#

Is it possible to pause the leafwinginputplugin from replicating to the server (e.g. when a player goes into the menu, or unfocuses the window)?

pine cape
#

I think I pause the replication if the ActionState is disabled, I would have to double check

#

I might not handle it correctly yet because that was added in the next non-released leafwing version. In the current one there is a ToggleAction resource you can use to disable the plugin

wintry dome
#

despawning replicated entities on the server doesn't seem to be despawning them on the client (server calls commands.entity(entity).despawn();). (on main) it successfully removes on all clients any players that disconnect, but despawning arbitrary replicated entities doesn't seem to replicate atm.

#

stick this in xpbd_physics server.rs movement() to demo:

// q: Query<Entity, With<BallMarker>>,
        if action.pressed(&PlayerActions::Down) {
            for e in q.iter() {
                info!("REMOVE {e:?}");
                commands.entity(e).despawn();
            }
        }
pine cape
#

This works for me on main

wintry dome
#

hm, time i did cargo clean and reset things then, sorry for false alarm

pine cape
#

No worries

mighty mason
#

Hey I was reading through the examples and I noticed that they use something called a connection manager? Just wondering where it comes from

pine cape
#

and that generally handles a lot of the networking internal stuff

mighty mason
pine cape
#

It's automatically added by the lightyear plugin

mighty mason
#

I was following the physics example in lightyear trying to replicate to my own system however it seems that when I added the replication of the server thing it appears that the physics got all messed up when they were before and I am not sure why

#

Also I am not sure what a predicted and confirmed entity is

pine cape
mighty mason
#

oh alright thank you this is very useful I should've checked here

#

wait so

#

Should the predicted and confirmed entity of the client exist on the client it's coming from?

#

or is that bad?

pine cape
#

A client should have a Predicted copy of any entity that is replicated from the server (Confirmed) if it needs to run client-side prediction for that entity

mighty mason
#

I think what I meant to say is

#

The entity on the client that I am controlling is the predicted entity

#

I think

pine cape
#

Yes

mighty mason
#

it is???

#

That's supposed to be the case?

pine cape
#

yea, you want your controlled entity to be predicted so that you can control its movements with no latency

#

but you don't have to

mighty mason
#

ohhh

#

I see

wintry dome
#

just tried the despawning thing again from a fresh clone, with cargo run client-and-server and server / client . ball doesn't despawn on the client when the server despawns it. am i going mad or is this a bug? you can see it log "REMOVE 1v1" when the server despawns it.

stiff quiver
pine cape
#

oh i was despawning the player entities; i see the same thing with the ball, hmm.

wintry dome
#

this is the first time i tried to despawn random entities on the server only, until now i let my Lifetime expiry systems do it in parallel on client & server. so unsure when it was working previously.

pine cape
#

ah it's becaue i'm using With/Without instead of Added.
It would work if I ran the system every frame, but i don't

#

actually it should still work even if i don't run this system every frame

wintry dome
#

looks like a potential job for observers in bevy 0.14?

pine cape
#

yes i was looking into that

#

I think I can change some things into observers, but there's some features missing

#

for example i'd like one observer entity to observer multiple components

#

but i think the component list has to be added before the observer is spawned, they can't be added afterwards

wintry dome
#

i expect lots more observer upgrades will land fairly soon after 0.14. looks like a useful api

#

works if i change handle_replicating_add to use Added<Replicating>. at least, the ball despawns fine

pine cape
#

really? i just found out what causes this and i would expect this change to fix it

wintry dome
#

i don't know if it breaks anything else, but replacing With<Replicating>,Without<DespawnTracker> with Added<Replicating> means the ball despawns on the client

pine cape
#

I wonder how come.. I see that with Added<Replicating> the system runs twice for the ball

#
2024-07-02T14:50:15.153473Z  INFO lightyear::server::replication::send: Replicate component was added for entity 8v1 sender.replicate_component_cache={8v1: ReplicateCache { replication_target: All, replication_group: ReplicationGroup { id_builder: Group(1), base_priority: 1.0, send_frequency: None, should_send: true }, network_relevance_mode: All, replication_clients_cache: [] }}
2024-07-02T14:50:15.155652Z  INFO lightyear::transport::websocket::server: Starting server websocket task
2024-07-02T14:50:15.155798Z  INFO lightyear::transport::webtransport::server: Starting server webtransport task
2024-07-02T14:50:15.289195Z  INFO lightyear::server::replication::send: Replicate component was added for entity 8v1 sender.replicate_component_cache={8v1: ReplicateCache { replication_target: All, replication_group: ReplicationGroup { id_builder: Group(1), base_priority: 1.0, send_frequency: None, should_send: true }, network_relevance_mode: All, replication_clients_cache: [] }}
wintry dome
#

that's odd

#

on the same tick?

pine cape
#

no it looks like different ticks

wintry dome
#

i would expect Replicating to need to be removed and readded for Added<> to trigger a second time..

pine cape
#

oh because we run it once in PreUpdate and once in PostUpdate

wintry dome
#

ha, yes that'll do it

pine cape
#

ok i have a fix

#

not too pretty though

#

in general i'll have to think of how restarting the server interacts with replication

#

but honestly do we even want to be able to restart the server dynamically and still be able to replicate the same world?

wintry dome
#

for me restarting the server would imply fully nuking the world state anyway i think

#

at which point i'd probably just restart the entire process tbh 🤷

pine cape
#

yea, maybe i should just remove dynamic server restart entirely

wintry dome
#

can't think when restarting the server should be necessary and preferable to a process restart

pine cape
#

maybe when the client acts as a host-server for one game

#

and then returns to lobby

wintry dome
#

restart the thread maybe

#

oh host-server is same app

#

remake the entire bevy app 🙂

pine cape
#

maybe

#

I merged a fix

wintry dome
#

thanks! out ATM will give it a go later today

split slate
#

Hi!

First of all, thank you for this crate and all the work going into building it. I’m happy to help out where I can if you’re looking for contributions 🙂

I was trying out the spaceship example and noticed that inputs aren’t registered when running in host-server mode (ActionState get_pressed() returns empty). I’ve been trying to figure out why this happens. The xpbd example works fine in this mode, and one difference I can see in the xpbd example is that the player entity is pre-predicted on the client, while in the spaceships example, the player entity is server-authoritative at the start. It seems weird to me that this would be the cause of it. Any ideas on why this might not be working in the spaceships example? Or any gotchas/pointers when it comes to host-server input handling in a more general sense?

Thanks,

pine cape
#

In general I think there are gotcas with host-server mode in general; it uses a set of 'tricks' to work correctly. (for example adding the Predicted and Interpolated markers even though there is no replication going on since everything is happening in the same world)

#

The main thing to understand is that the client world and server world are shared in host-server mode; so at first glance I don't understand why input-handling would not work. When you're adding the InputMap and ActionState to the client-entity, you're also adding them to the server-entity, since they are the same

#

ah it's because the example uses the InputBuffer to handle inputs instead of using the ActionState directly

#

I probably should add some special handling for host-server mode where we make sure that:

  • there is 0 input delay for the client (or maybe not, if we want the game to be more fair between the host and other players)
  • we still add an InputBuffer that is updated every tick using the ActionState
pine cape
#

@split slate I fixed input handling for spaceships in host-server. (https://github.com/cBournhonesque/lightyear/pull/525)
Looks like it doesn't work when a second client connects though, that would be something else to look into

GitHub

Inputs were not working in host-server mode for several reasons:

the Controlled component was not added on local-client entities
the ActionState on the server was updated from the InputBuffer even...

mighty mason
#

Hey I was a bit confused about some things I was editing the angular velocity of a entity on the client and noticed that unlike the position it wan't doing rollback I don't understand why?

#

Would it be ok to adjust update the angular velocity on the client?

pine cape
#

Make sure that it's added with ComponentSyncMode = Full and make sure that your physics logic that updates angular velocity runs on both client and server

mighty mason
#

Is that what I want tho?

#

Whats wrong with just letting the client replicate it's physics to the server?

pine cape
#

nothing wrong with that, you have to decide what you want:

  • server-side physics with only interpolation -> easy but has input delay
  • server-side physics + client-side prediction -> a bit harder, but not delay
  • client-side physics only, and replicate to server -> easy, but it's easier for users to cheat.
mighty mason
#

Oh so it would allow people to edit their like position and stuff I get it thank you

wintry dome
#

i seem to enter a death spiral pretty easily in my basic client/server game, but not sure what triggers it. tracing seems to show a lot of empty space in the longer frames, so not sure where all the time is going. any advice on diagnosing it?

#

suddenly my frame time jumps from 6-7ms to wildly fluctuating much larger numbers

pine cape
#

Hm i have no idea what this empty space is

#

apart from that do you see anything else that seems to take a lot of CPU?

wintry dome
#

weirdly nothing seems to take very long

#

hm or ordered by total time

pine cape
#

seems fine.. nothing shows when you zoom in on that section?

wintry dome
#

nope, nothing in the trace. just a big empty space

#

the actual stuff in the trace at the end of the frame only takes 4-5ms, but something seems to be causing havok somehow

mighty mason
#

hey when replicating other players to the local client their transform isn't synced to their position with xpbd and I'm not sure what to do about it? Should I just also replicate the transform?

pine cape
#

They should be, there is a system that keeps position in sync with transform

mighty mason
#

Yeah I don't know why it's not working

#

when they join at first they don't have it so I manually add it to them I guess that's what messing them up

#

If I add a transform component and tell it to just replicate once so that it comes with everything else do you think that would fix it?

#

because yeah when I add it manually it's not synced for some reason

#

yeah that seemed to do it weird

mighty mason
#

how would you go about replicating the mouse position to the server?

#

I'm trying to make a system where the character automatically looks towards the cursor position and it's replicated and I'm trying to figure out how to do that

pine cape
#

i'm not sure it's fully correct, because the mouse position sent to the server is computed relative to the client's window, but i think it should be the absolute world coordinates instead

mighty mason
split slate
# pine cape <@1037832956259094618> I fixed input handling for spaceships in host-server. (ht...

Hi! The "Controlled" component seem to be replicated for the hosts player entity. I think this might be the reason the example behave strangely when a second client connects. Overriding the replication rules for that component makes things work better. Is there a case where you would want to replicate "Controlled"? It looks like it would only be usable for the local peer, but i don't really have the full picture so maybe it makes sense 🙂

Thanks,

pine cape
#

Thanks, you're right! I've made the change to stop replicating [Controlled] in host-server mode

#

There seems to be a couple things not working (the test showing the latency), or the score; but those are fairly minor

mighty mason
#

I can view that they are being updated when I print out the cursor coordinates on the client but not on the server. Which is strange because in the exact same same enum of registered inputs I can view that the other inputs and that they are being replicated.

pine cape
#

Maybe it's some ordering issue, did you make sure you also run the system in the same system set as in the example?

mighty mason
mighty mason
#

Oh yeah it didn't seem to help

mighty mason
#

oh wait it did fix it nvm

#

btw can you put an entire vector into the DualAxis thing? or can it just be a normalized vector?

mighty mason
#

oh looks like you can just put any length vector into it ok

pine cape
#

@wintry dome I tried to upgrade the spaceships example to 0.14 but for some reason the walls are flickering on each rollback

#

I wonder if I somehow broke something

#

Also it seems like my code that adapts the input_delay in function of the network conditions doesn't fully work:

  • what if suddenly the amount of input_delay goes from 5 to 4? then the same input for a given tick could be overwritten, so the server might receive input A, and then receive input B later on for the same tick
  • what if suddenly the amount of input_delay goes from 4 to 5? then there could be a tick with no input

I would have to think about these edge cases

pine cape
#

In general I feel like the example is more buggy now. I wonder if it's because of:

  • avian upgrade
  • lightyear upgrade to use observers in some places (replication, prediction, etc.)
  • input handling upgrade
#

the basic xpbd_physics example with only 1 client is also getting rollbacks when hitting the ball, which means either there's a physics bug (maybe some ordering constraint is missing) or a replication bug

pine cape
#

After a big of debugging, it may just be that avian is less deterministic than xpbd was? I don't see any obvious issues with inputs/replication

wintry dome
#

woop lots of changes and upgrades, looking forward to trying it out. travelling the next couple of days tho so not going to be online much

stiff quiver
#

@pine cape i can only imagine the impact this had 😂

pine cape
#

unfortunately the 3 biggest upgrades are not really usable because they don't work in edge-cases:

  • replicate changes since last_send instead of since last_ack (to reduce bandwidth), there are some edge-cases where those ticks should be tracked per (entity, component) instead of per replication-group
  • delta-compression: same edge-cases as above
  • input-delay-tick changing with network conditions: have to think about what happens if the input delay changes

but yeah overall i'm happy to finally merge all these changes

pine cape
stiff quiver
#

:P

paper sage
#

I just started using lightyear recently. Am I right in thinking calling add_interpolation(ComponentSyncMode::Full) without adding an interpolation function for a component is invalid?
I'd accidentally done this and was getting crashes intermittently (only when interpolation was actually needed I assume), and was thinking it might be good if the protocol configuration api made this hard or impossible to do, or if there was an optional plugin to validate the protocol configuration at startup during development.

pine cape
#

Hm yes you're right, I should update the API to make this impossible! For now i'll just panic if no interpolation was provided for a ComponentSyncMode::FULL component

wintry dome
#

@pine cape i don't see the enhanced-determinism avian feature enabled by lightyear. that might account for demo weirdness?

pine cape
#

ah didn't know about this feature, enabled it but it didn't change much

dull lion
#

hey, can we gate bevy_web_keepalive behind a feature? it is causing my project to pick up an old version of glam

#

(and i don't target wasm so it is not useful to me)

dull lion
#

it is slower than the default and should not be needed for lightyear i think

pine cape
#

Hm, i wanted to check if this was causing the extra rollbacks (since in xpbd there were no rollbacks even when the ball hit the walls)

#

@stiff quiver could you upgrade bevy_web_keepalive to 0.14?

#

i'll gate it behind wasm

#

it should already be feature-gated to only be present in wasm

dull lion
stiff quiver
stiff quiver
#

@pine cape #crates message

#

if u run into any issues lmk, didnt find anything during testing tho

pine cape
#

great

stiff quiver
#

@pine cape wouldnt it be possible to reuse the client entity as a camera? I feel like just inserting the camera components / bundle should work fairly well and you dont have to map the client to the camera anymore

pine cape
#

What do you mean; possible to do what?

stiff quiver
#

mainly to avoid having to keep a reference to the camera (ie through a hashmap or similar) so it can be mapped to the client entity

#

similarly you dont need to keep track of the client id / entity on the camera

pine cape
#

But what is the context? there's no reference to the camera currently

stiff quiver
#

in a general context, i was just thinking through how camera stuff could work with lightyear

pine cape
#

but the server doesn't have any rendering, so it doesn't need any cameras, no?

stiff quiver
#

if that makes sense

#

3d would be a bit harder

pine cape
#

Sure I think this could be possible. I guess the user can do that by themselves, and them use the camera to update the visibility via the NetworkRelevanceManager

stiff quiver
#

yep this is totally a user responsability, i was just thinking about how that could be efficiently implemented

#

just wanted to know if there are any downsides to replicating the client entity to the client, but im guessing no, since its just a regular entity

pine cape
#

yea it should be fine

inland mantle
#

"is it possible to have a spectator view on wasm of other players on wasm with like lightyear networking" @pine cape didnt see you replied before, so you think its feasible ? and yes it would be like the player can also join a game in spectator for example so he see other players but cant do anything
is it necessarly a networking question? Like even if its a simple game without multiplayer i guess my question need to involve networking to do that

pine cape
#

Yes I think it's possible in lightyear. It's not necessarily related to networking, you can just replicate the world to an entity but not give them anything to control

inland mantle
#

how can the entity view the world in graphical/rendering way

pine cape
#

I think any of the examples in the lightyear repo work. The world is replicated, you can then place a Camera on the client

mighty mason
#

Does lightyear support avian yet?

#

or have the feature

#

actually should I bother using avian or should I stick xpbd for now?

pine cape
#

Yes the latest release support avian

mighty mason
#

oh cool

#

thanks!

#

were messages removed in the new version of lightyear?

pine cape
#

Nope, still there

storm gulch
#

Hi, lightyear doesn't resend message to host when mode is Mode::HostServer? Bevy replicon has this behavior, which is really convenient

pine cape
#

You mean that if the client-host sends a message to the server, they should also receive their own messages? If so I agree, I can add that

storm gulch
#

I mean if a host send a message to all clients or to a client which is itselft then lightyear should resend the message, because the host also is a client. Just trigger MessageEvent<T> directly.

pine cape
#

yep!

#

working on it

storm gulch
#

Thanks, hope you can fix the docrs deploy as well. Building docs locally take so long 😭

pine cape
pine cape
storm gulch
raw thorn
#

is it possible to register a bundle for replication?

pine cape
#

Nope it's not possible currently

#

What would be your usecase, to have a different serialization when replicating a bundle?

pine cape
#

I see 2 main options:

  • change messages to send_message(m) and update resource replication to not use messages but instead to use components (however this was causing issues for bidirectional replication)
  • create local channels to still send serialize and send packets in host-server modes. The only packets sent would be messages (no inputs, replication, ping, etc.)

I guess a third option would be to serialize/deserialize the message even when running in host-server mode

pine cape
#

Then i'll publish a second PR that enables sending messages from the server to the local client

#

Thanks for mentioning this btw, it's pretty big missing feature. I never really noticed because I rarely run examples in host-server mode, and none of the examples send messages

storm gulch
#

Is there anyway to pause/resume InputMessage when using Leafwing feature? For example I dont want to send input when the game is paused.

pine cape
#

You can use https://docs.rs/leafwing-input-manager/latest/leafwing_input_manager/plugin/struct.ToggleActions.html to disable an Action.

However in leafwing's main version this has been change to a https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/src/action_state.rs#L536 function on the ActionState.

Currently packets are still being sent containing empty inputs even if the ActionState is disabled (I don't want to support the old way since I'm going to have to support the new method in the next version), but I plan to also pause the InputPlugin on lightyear when this is the case.

GitHub

A straightforward stateful input manager for the Bevy game engine. - Leafwing-Studios/leafwing-input-manager

pine cape
#

I fixed the message issue, now there should be no issues sending/receiving messages with the local client in host-server mode

storm gulch
pine cape
#

did you upgrade to the main branch?

storm gulch
pine cape
#

I tried it in the simple_box example, it seemed to work for me

storm gulch
#

I will try to create minimal reproduce tomorrow, it's late here

pine cape
#

SG. FYI I'm running cargo run -- host-server in examples/simple_box and then I press M to send a message from the server to all clients

#

maybe refetch main also

storm gulch
#

It work now, turn out that I didn't call commands.connect_client()

pine cape
#

great

stiff quiver
#

@pine cape what could i do if i want my camera transform to be interpolated with a exponential smoothing function but every other transform with lerp? thonk

pine cape
#

Or when adding a component to the registry you should be able to specify multiple interpolation functions?

#

but why is your camera replicated btw? that's fairly unusual.

stiff quiver
stiff quiver
pine cape
#

so the client transform is replicated to the server so that the server can not replicate anything outside the camera?

stiff quiver
#

For anticheat purposes

fervent karma
fervent karma
#

Actually looks like quite a few of the links to docs are outdated on that page, and presumably on other pages too

pine cape
#

Yes it's hard to keep the book links up to date. Thanks for the report!

fervent karma
#

Yeah, I totally understand, it's not a huge deal, but figured I'd make you aware anyway ☺️

#

I can probably make a PR to fix em up later

summer finch
#

I'm currently migrating from replicon to lightyear, but I've run into a wall with my main menu of all things. What would be the best way to configure lightyear from a main menu? As in, the player can configure the network settings in the main menu (this includes choosing whether to run the game in ClientAndServer mode or Client mode) before starting the game.

My first idea was to run the main_menu as a separate app which sends the configuration to the main thread upon exit, and I use that to setup the lightyear plugins in the main game app. However, apparently winit doesn't like when we start the winit event loop twice in the same program.

#

I'm probably missing something obviousthonk

pine cape
# summer finch I'm currently migrating from replicon to lightyear, but I've run into a wall wit...

@stiff quiver that's incorrect, everything is actually updatable at runtime, and is controlled via runconditions!

@summer finch If you want to do this, you should start the client app with both the ClientPlugins and ServerPlugins added. host_server mode is only activated if those 3 conditions are met:

  • server and client plugins are present
  • Config.shared.mode = HostServer
  • Server is started

So you should add the lightyear plugins right away (Client and Server). Then after the player configures the mode, you can update the Config.shared.mode settings.
The settings are reloaded whenever you run commands.start_server() and commands.connect_client(), so it should be fine to do this at runtime.

Actually the lobby example (https://github.com/cBournhonesque/lightyear/tree/main/examples/lobby) shows exactly what you want, where the network config is updated at runtime.

GitHub

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

stiff quiver
#

oup sorry bout that

pine cape
#

nw

summer finch
river perch
#

Hey all, quick question regarding replicating entities to clients: When spawning, let's say a player, on the server, we replicate some of it's components to the clients (Id, Transform, etc.). I assume things like Mesh/Sprite are usually up to the client to to add on once the player entity is replicated for the first time? Is there a standard way to do that in lightyear? Is there some event I should listen for on the client? I couldn't find an answer in the book. Thanks!

pine cape
river perch
#

Exactly what I needed, thanks!

harsh crescent
# pine cape > When I send a message from the netcode client, both that netcode client and th...

I finally have some code to reproduce this, now updated to the latest released version of lightyear and bevy. I confirmed that all the same things are still happening. I'm also having trouble replicating changes to components from client to server, if you don't mind taking a look at that as well. I looked around at some of the examples, but I could only find ones that replicate inputs from client to server, and then the server sends down the actual state of the components, or I'm misunderstanding them. https://gitlab.com/TogMP/lytest

river perch
#

What would be the best way to handle mouse motion to control a character's rotation in an FPS game (server authoritative)? I'm worried about communicating floating point mouse motion data to the server, is that a reasonable fear? I was thinking the client could actually be authoritative, just for the rotation of its character (as it is usually completely valid for the client to rotate it's character anyway they want without the server's "ok"), and replicate that along with the WASD inputs to the server? Does this make sense to anyone else? I'm still very new at working with lightyear and networking in general, trying to get a handle on input handling!

pine cape
#

I think you could either send the cursor position in world coordinates (Vec3) to the server, or compute the rotation on the client and send that to the server.
I think sending the rotation is probably easier in this case, yes

pine cape
stiff quiver
pine cape
#

@harsh crescent
for the 1st problem of client->server replication, it's because your update_component system (https://gitlab.com/TogMP/lytest/-/blob/master/src/client.rs?ref_type=heads#L127) updates the entity that has Controlled component (which was replicated from server to client). You're not updating the ClientControlled entity.

If you want to update the client->server component, you need to query the entity that you created here: https://gitlab.com/TogMP/lytest/-/blob/master/src/client.rs?ref_type=heads#L88

The "ClientControlled" entity on the server is not controlled by the client; it is server-authoritative. Actually on the server you should have 2 entities, one that is client-controlled and one that is server-controlled

pine cape
#

For the message issue; first you're not supposed to add the SharedPlugin yourself, it's added already as part of ClientPlugins or ServerPlugins.
Then if in your Cargo.toml you use lightyear = { git = "https://github.com/cBournhonesque/lightyear", branch = "main", features = ["steam"] } (to use the latest 'main' version), then it works 🙂
(you need to run cargo update first)

river perch
pine cape
#

You should probably use steamworks-rs for this.
In fact when using lightyear with steam you can provide your own steamworks-rs::Client, so you can keep using that

river perch
#

okay cool, thanks

pine cape
#

however you can also use lightyear to directly send messages to all connected clients (including steam clients), so you could also use that. You would have to structure your bevy app to include a 'lobby' stage, where clients can still send messages to each other (something like https://github.com/cBournhonesque/lightyear/tree/main/examples/lobby)

river perch
#

Does that example use steamworks matchmaking lobby system internally? I've been using that example as a reference, but I am a bit lost when it comes to the steam part of it 😅.

I have a simple game that uses a local dedicated server like that example to host lobbies, but transitions to one player hosting once they start the game. I'm trying now to integrate with steam and replace my dedicated lobby server with steamworks matchmaking backend. It's going well, but I've hit a roadblock when it comes to sending messages to players through steamworks matchmaking

pine cape
#

It uses steam only if you set the Netcode on the client to use steam (in the settings file)
Basically lightyear abstract over different transport layers (websocket, webtransport, steam, etc.) so all the lightyear apis (send_message, replicate components, etc.) just work out of the box with any of them.
You only need steamworks-rs to do steam-specific stuff (achievements, etc.); if it's just sending a message to connected clients, you can do it via lightyear

river perch
#

Oh okay. That's great to hear. I will try setting my project up that way. I appreciate the help!

#

The example readme says to run the dedicated server as well as the clients, is this also required for the steam setup? I am hoping to avoid having a dedicated server managing lobbies, and instead use the steamworks matchmaking lobby backend

pine cape
#

Host-server mode (client and server in the same process) should be possible with steam backend I believe

wintry dome
#

i've been shooting asteroids and smashing them into smaller chunks. i very soon end up with a lot of entities, which eventually causes a death spiral due to each update packet being very large. starts to suffer around 2000 entities in debug mode with optimisations. i currently have everything in the same replication group. after the initial spawn, I want to reduce the frequency that asteroid entities send updates, but still send player updates every time. things should be deterministic enough for that I think. what's the approach with lightyear for this? i don't want interest management (for now). do i need to move asteroids into a separate replication group?

stiff quiver
pine cape
#

Yes I think you should have a separate replication group for the asteroids, with a separate replication_interval.
Are the asteroids predicted as well? You might get more rollbacks or some rollback errors (since parts of the scenes to rollback won't have a history for the specific tick you want to rollback from), but maybe that's ok?

If it's ok, then there might not be a need for replication-groups at all

wintry dome
#

everything is predicted, to keep collisions between players and asteroids non janky. as long as i send the asteroid spawns immediately to all players (which will likely cause a rollback) they should get client predicted fine, so future, less-frequent updates for asteroids wouldn't necessarily cause rollbacks.

#

"priority accumulation" stuff talks about priority for replication groups, so presumably i cant ignore groups and just give the asteroid entities a low prio?

#

ie, it needs to be per replication group

stiff quiver
wintry dome
#

interpolating asterouids will make collisions with players janky, because players are client predicted (ie, player timelines would be ahead of asteroids if they are interpolated)

stiff quiver
#

hm weird, i never had that issue even tho i have that setup

wintry dome
#

it'd be like trying to kick a ball, except you're looking through goggles that cause a 100ms delay in what you see

stiff quiver
#

Might be the frequency of simulation / replication idk

wintry dome
#

or rather, it shows you the asteroids in their position 100ms ago

#

i'll try a replication group just for asteroids with a low rate

stiff quiver
#

actually cant we make the replication rate dependant on movement speed and avians sleeping functionality? Maybe that could help

pine cape
#

The other option is that I have implemented delta-compression (i.e. only replicate the diffs between 2 component values) There are currently some caveats to use it. But then you'd be able to quantize the values replicated so that small distance changes take less space to replicate

wintry dome
#

that could potentially save quite a bit, but i would like to reduce the replication interval first

#

hmm, if i put all asteroids in the same replication group, they'll all be updated at once on that tick (depending on the interval)

#

might it be better to give each asteroid its own group then?

#

to spread the load of replicating loads of entities

pine cape
#

i'd try first with all of them in one group

#

there's a cost to having many groups, because you need to replicate the ReplicationGroupId every time

wintry dome
#

i set the Replicate.group field for asteroids to ReplicationGroup::new_id(2).set_send_frequency(Duration::from_millis(500)), and they appear to be snapping to new positions every 500ms, as if client prediction isn't happening

pine cape
#

but they have the Predicted component?

#

they are snapping into position into a past position (the server position), without any prediction?

wintry dome
#

hm, and sometimes when i start my server+client i don't see the asteroids on the client, something not fully replicating

pine cape
#

that's only when you change the send-frequency? there might be some edge cases or bugs related to that, i haven't tested it much

wintry dome
#

changing the group was the only thing i did yeah, with the new send freq

#

definitely something fishy going on