#lightyear
1 messages · Page 4 of 1
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
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
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
API documentation for the Rust LeafwingInputConfig struct in crate lightyear.
Also your client_send_interval=Duration::default() right? client needs to send input messages every frame
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
Oh so your issue is actually that left remains pressed for server_send_interval? it doesn't stay pressed indefinitely?
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)
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!
I tried it on the bullet_prespawn example with send-interval = 1000ms but it worked for me
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)
i'll make a new example based on the xpbd one to share 👍
i duplicated the xpbd demo and modified it a little. players are now triangles, spawned by the server. https://github.com/RJ/lightyear/tree/main/examples/spaceships
this all seems to be unrelated to pre-spawning players no? once the prespawning is complete than you're in the same situation as if the server had replicated the entity for you
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?
You're on lightyear main? I might have broken some of the replication logic
yeah on main
ah, yeah. works fine if i run my demo atop 303a2f00201a0e55bdd84fb940e955ee47387187 (and pick the sync-controlled commit)
ah i thought i had merged this fix but i hadn't: https://github.com/cBournhonesque/lightyear/pull/399
yep that fixes my issues, thanks
server spawns the players, and i changed the movement to use physics forces
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 🤪🔨
What's the best way to provide hooks for user logic? I have this PR: https://github.com/cBournhonesque/lightyear/pull/401
to let the user accept/reject incoming connections, but i'm not sure if I designed it correctly. Does anyone have an opinion? (cc @bronze elm )
Looks very cool! maybe i'll build on this to have a 'shooter' style demo. (where the bullets wouldn't get replicated, and player kills would be server-authoritative)
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)
This is an example of how a minecraft plugin would work for whitelisting based on name:
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
and the default implementation would be just to accept all
Yes eventually we can have Denied reasons, but i want to start with the simplest impl.
Thanks for the link
doesnt seem that useful if the only info we get is the clientid
maybe this would make more sense in a system
ah also, shouldnt this be done on the server that creates netcode connection tokens anyways?
yea but it could be convenient to have the game server do this as well; most users won't have a separate backend I think
btw I have an issue with bevy_web_keepalive: https://github.com/cBournhonesque/lightyear/pull/403
i guess yea
but idk i feel like it should be handled when creating the connection tokens, not when the user is already connecting imo
ah
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
maybe 0.3.1
v0.3.1
released
@pine cape u can just use new version, no changes required on ur part
works
@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
I think that's related to the steam; I created an issue for this in the past: https://github.com/cBournhonesque/lightyear/issues/243
Amazing! So what does p2p do exactly? One of the peers has to act as a server?
Basically, yes.
But as it is over steam, we dont provide any ip address, but only the steam ID of the user we want to join to. Then via the steam relay Stuff happens and we're connected
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 :)
Aah i see
I guess the main benefit is, that users don't have to know any ip adresses and don't have to do any port forwarding but are just able to join
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
Ok thanks for the summary!
Of course!
Now if you excuse me, i'll go and Pass out in my bed. Work starts in a few hours again 🥲
Aha of course; good luck 💪
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
Love getting those updates; looking great!
Cool, great job!!
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 🤔
i'm pretty sure i've seen lightyear be unhappy about despawning a replicated entity on both the client and the server, though i haven't investigated further
This issue stopped appearing for me when I stopped despawning on the client https://github.com/cBournhonesque/lightyear/issues/324. Might be able to reproduce it by despawning a bunch of entities on both server and client, if the issue still occurs
I suppose I could hide it on the client and remove the colider safely, let the server clean it up
pretty cool! do you plan on making a PR eventually?
Also in a real game would bullets be replicated? (because of latency concerns) I was thinking no actually they might have to be, for interpolation on the other clients
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
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
It should be ok; if the server sends a "despawn this bullet" message and the entity doesn't exist on the client, it should just silently do nothing. But the safest would be to stop replication on the server (remove the Replicating component) and then despawn the entity. Then there will be no despawn message sent to the client
yes i'd like to make a PR, want to get shooting working well first
i see, so just make the server not replicate the despawn. that'll do nicely
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
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)?
@jade ember what would be the difference between the server p2p connection and this? https://partner.steamgames.com/doc/features/multiplayer/steamdatagramrelay
when player 1 fires, player 2 spawns the bullet player 1 fired twice: once when the server replicates the bullet entity to it, and once when it spawns a pre-predicted version on behalf of player 1 due to having the Fire player action for that tick. i could avoid spawning as a result of remote players pressing fire, but that would result in a worse simulation overall
seen around 0:12 in the vid, when pink fires
According to the steam docs they should pretty much always be used together. Though I guess if you don’t use it you still get the benefits of being able to connect to another user via their steam id instead of their IP
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)
But basically it looks like using the p2p api (or the dedicated server api) from steam networks IS using the SteamDatagramRelay under the hood?
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?
Only if you turn on the relay mode
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.
yes I was wondering if something like that would make sense.
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?
1 tick should always be enough I think
also i'd like to understand why i've never seen this in the bullet_prespawn example, let me test it now
should I check for it manually before spawning, or should I spawn anyway and lightyear magically removes it if the hashes match?
ok
In case 1 lightyear does the removal; for case 2 I think you should wait for a patch, I wouldn't expect users to deal with something like that
yes ok, feels like something that should be taken care of if I use prespawnedobject
ah i've never seen this because in bullet_prespawn the remote players are interpolated, not predicted
ah, makes sense
you can turn it off? AFAIK it's always using the relay
whenever you call the connect api, it first inits the relay
that's what we're currently doing
so from my understanding, yes, that's the case
steamworks btw also recommends to init that on startup of the game. with our current implementation i can't easily do that in lightyear, so atm i'm just doing that in by myself whenever the steam client gets added
I was under the impression that you had to call this https://github.com/Noxime/steamworks-rs/blob/a37a81e6d15c5c23ba670c9a9c31acca10654aca/src/networking_utils.rs#L64
Btw what's the status of the PR? let me know when I can review
Once @jade ember submits his fix I'll do a cleanup pass and then I suppose it'll be good to go
i created the merge request yesterday, you should find it on your branch :)
huh weird, no notification about it
no worries!
and i mean its just one line change
so the merge request wasn't even really needed tbh
credit where credit is due!
i'll have a look at this again later tonight or tomorrow then
@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?)
I think in that case you would have to tick it in the background at 60Hz, but yeah it should work. Server and client are in the same world in host-server mode
oh alright
thought it was like a sub app or a second app
@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
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)
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
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
I updated my branch here https://github.com/cBournhonesque/lightyear/pull/346 and marked it as ready for review. Even if it needs some changes to work better with lobbies it's probably a good start for further iteration
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
let me actually run one of the examples using your branch and steam p2p... i honestly only tested if we connected, didnt test if anything else works!
got a bit late yesterday :)
also btw @pine cape i noticed that a lot of the run conditions aren't pub but only pub(crate)? why's that?
i'd really like to use is_connected for example, and the others too
https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/shared/run_conditions.rs
yeah I just forgot to make them pub
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
awesome, thanks :)
@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
just realized, the only real change is in the common crate so all examples should actually work?
for anyone interested in testing it too, you have to put in the steam id of the host you want to join https://github.com/msvbg/lightyear/pull/4/files#diff-d1b28d27f836312248a4aa3c8c537adbd100c006ce6a38785dff930fdd34bc37R334
this needs to be only done for the client.
two different devices and accounts are required
i was also unable to reproduce https://github.com/cBournhonesque/lightyear/issues/243
no idea why
great, thanks for testing!
yes exactly
@jade ember maybe easier if you do that in a follow-up pr to lightyear
great news that the examples are working!
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.
Your ships are not PrePredicted entities right? they were created on the server?
Do you see these logs every time?
ships created on the server https://github.com/RJ/lightyear/blob/main/examples/spaceships/src/server.rs#L141
i think this is happening reliably every time
just going to rebase upstream and retest
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
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
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?
what do you mean, it's a public repo
the spaceships demo is in my main branch at the mo
ah right idk i got confused by some git errors
ok
Yeah as you say, if it is the case that it's thread-safe then it shouldn't be necessary. Hopefully this gets fixed upstream in steamworks-rs
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
@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
yep, sounds good
cool! and i agree with @dull lionhere
excellent, thanks 👍
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.
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.
Interpolationis only used for non-predicted entity, and it's just to visually interpolate components between the server-snapshots. It has no effect on predicted entitiesCorrectiondoes what you want: it lerps the snapback smoothly over a few ticks. To enable it you need to add a correction fn (oradd_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
ah! thanks for the explanation, i didn't realise it was changing it just for rendering
@pine cape if we dont supply the server with a NetcodeConfig::private_key, will it assume that every connect token is ok?
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?
yes exactly, the backend server generating the tokens need to share the same private_key as the game servers
alright awesome
i just discovered how neat tmux is for running client and server watchers in parallel
what do you mean by watchers?
trunk serve basically
and i run it alongside cargo watch for the server
like client/server processes? yes I always have tmux on 🙂
very neat :P
does this fix an overshoot? I am trying to get client side physics to work right now and when I release the key it seems to overshoot the position (probably based on lerp'd velocity) and snap back
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
that's because there's something wrong with your client-side physics so the snap back is a rollback
You don't have to implement Linear for Position, you can directly implement any interpolation function! Actually one is already provided for you here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/utils/bevy_xpbd_2d.rs#L10-L10
btw i dont see transform and globaltransform being replicated, dont they need to be on the client too for xpbd to work?
here i mean
wrong link
no xpbd keeps transform and Position in sync
but it doesnt get automatically added no?
i think so at least
it does, if you add Position, transform will get added
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
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
yes you should be able to, as long as you specified an interpolation fn for Position
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?
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
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
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.
nope .add_interpolation() is to enable the use of Interpolated entities (sync component to the Interpolation entities)
I don't think you even need to call .add_interpolation here, you can just register an interpolation fn
why does the xpbd example do both? https://github.com/cBournhonesque/lightyear/blob/main/examples/xpbd_physics/src/protocol.rs#L177
because I have an option to interpolate other players (if predict_all = false in the settings), whereas you're predicting everything
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
yes I think it should be fine; it's also cheaper because then you're not adding systems dedicated for client->server sending
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 ?
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
got it, thanks
and the add_correction_fn is only used for predicted entities, to blend in rollback errors (outside of fixedupdate, for rendering)?
I should probably update the docs, I guess it's not clear enough
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 🙂
any suggestions for accessing this value btw? seems buried in the actionbuffer atm i think
I think it's only accessible in the ActionDiffBuffer Component (it's updated here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/client/input/leafwing.rs#L1026-L1026)
There is an end_tick function: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/inputs/leafwing/input_buffer.rs#L380
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
ah didn't notice it was private; yes feel free to change the visibility!
ok
so if you wanted to run physics on remote players, that should mean making them predicted and use visualinterpolationplugin for best results? I didn't realize that plugin isnt supposed to work on interpolated lol. The setup im working with right now is predicted local player + interpolated remote players
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
@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?
it should be propagated to netcode now, but i'll have t ocheck
issues say otherwise. Dunno.
@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
I don't think it's related to netcode
maybe 3 is a coincidence; like the time it takes to reach an internal timeout or something
Alr, i sadly cant really help with testing till monday, but it would be worth testing if we reach the new WebTransport ctor at all (in js)
@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
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
right..
hm
cant we just put a flag at the start of the packet to identify it as a compressed packet?
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
yeah i was thinking about that
@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 
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
ah
ye ok that makes sense
ah ic now, the examples have it as a config option
thought they enabled both
yes exactly
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?
you have to implement MapEntitites in the protocol, like here: https://github.com/cBournhonesque/lightyear/blob/main/examples/lobby/src/protocol.rs#L114
I thought that only worked for components, but I just was about to delete that question since I found in the example that it works for messages too! Thanks!
lightyear feels kinda like magic so far
the rendered book isn't loading for me, but here's the book section: https://github.com/cBournhonesque/lightyear/blob/main/book/src/concepts/advanced_replication/map_entities.md with an example of using it with a message
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
it'll automatically get the Controlled component on your client i believe
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
here's where the server spawns the player for a new connection: https://github.com/RJ/lightyear/blob/spaceships/examples/spaceships/src/server.rs#L129
note the controlled_by: ControlledBy... bit
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.
There is an example for that iirc
"Auth" or smth
oh thanks, i'd missed that
And you want your game server to be the same machine as the website right?
yeah that seems easiest for testing
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
I see
the PostUpdate visual systems need to be order with transform propagation
will make an issue
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?
VisualCorrection is done before TransformPropagate already
VisualInterpolation has no ordering
i'll add it now
ok
hopefully this should work https://github.com/cBournhonesque/lightyear/pull/451
maybe you can try it before i merge?
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
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
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),
);
ah right, you might want to render after TransformPropagate
or just render in Last to be sure
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?
yes maybe, but in any case you would have to render after TransformPropagate
and lightyear operates before that
ok
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
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
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
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)
Is there a better way to be thinking about my problem? The core is that I just want clients to be able to tell the server who they'd like to shoot. I could maybe replicate my own ID component or something, but what's the 'lightyear-ey' way to do that?
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.
@pine cape how difficult would it be to integrate leafwing abilities with lightyear? 
I'm not too familiar with it, but I think you can already do it no? since the leafwing_inputs are replicated
hmm alright
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
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)
hm you're right, that's strange
the Channel's direction is not used right now, only the direction that is set when the message is regsistered
i think it should be registered as bidirectional anyway, since servers can rebroadcast inputs
you're right
I might just remove it, since the direction is set in the protocol directly
@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
sounds good - i was looking at that recently, trying to see if i could get it to send packets once per fixed update. but a configurable timer for replication separate from other channels is great.
(to transmit a packet per fixed tick now, i would send it in fixedupdate to a channel with a 0 millis send_interval, and it would be sent in postupdate)
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.
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.
Yes something might be wrong. Input delay used to work in the xpbd example but maybe I broke something, since there's no unit test for it. I'm refactoring the input code right now also, so maybe wait a bit before digging into the code
ok
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
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. ...
awesome!
congrats on landing that, very nice fix for fixed update dwellers 😎
@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)
amazing, i'll give it a try (probably tomorrow) and let you know how i get on 😃
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.
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
@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?
Nice, so no need to do any special handling for remote players' inputs when spawning bullets?
I guess since we get the inputs 'faster' than replication, we spawn the bullets first with PreSpawnPlayerObject, so the matching process works fine because the replicated bullet arrives later
same I really want to try it lol
It's not really usable TBH, i'm not very good at this web stuff
Edgegap works well for hosting the game server ; but the webserver part is lacking. It should do:
- tell edgegap we have a new connection request
- wait for edgegap to spin up a new container or give me an existing session, with a server_addr
- use the server_addr to generate a ConnectToken
- send the ConnectToken to the client so that the client can connect to the game server
no special treatment needed now for spawning remote players' bullets now - i provided my own PreSpawnPlayerObject.hash, in case two players fired on the same tick since i wasn't convinced the default hashing strategy would handle that
ok, i'll read up on the edgegap api and see what i make of it
hmm I should verify it works when inputs arrive late. may need to check on the client to avoid spawning something the server already replicated
Btw I have this branch: https://github.com/cBournhonesque/lightyear/pull/464
that can be used if you want to test against bevy-0.14
(Some examples don't work because their plugins don't have an upgraded version)
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
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
So I think the ideal way would be for the client messages/components to automatically apply the MapEntities from the client entity to the server entity, but only if the server is the Authority on that entity (i.e. initiated replication). I have to add a stronger concept of Ownership/Authority in lightyear before I can make that automatic
Ahh interesting
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
I don't we have turn-based game examples, but in general turn-based should be much simpler than real-time, you can just send invididual Messages between the client and server and react on them
That's about what I've been doing. So far the entity mapping on client-to-server messages are the only things that don't feel first-class. I'll let you know how it goes 🙂
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 🫤
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
yeah it's supposed to be portable, i've probably done something daft. will double check it later, swapped in an xor for now.
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?
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
No, only components with ComponentSyncMode::Full participate in rollback
yeah normally connections with a different protocol id are rejected
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.
Awesome!!
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.
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
I plan to provide the 3 settings described here: https://www.snapnet.dev/docs/core-concepts/input-delay-vs-rollback/
which should cover what you want
cool, makes sense
Otherwise most of the time sync stuff is in this file: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/client/sync.rs#L269
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
lightyear also runs on upstream throttle
Maybe try adjusting this setting here? https://github.com/cBournhonesque/lightyear/blob/d68a2f08d0775d85230644285507509aeeb205c8/lightyear/src/client/web.rs#L24
the App.update() runs only once a second when the tab is in the background, currently
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
Yes, I switched most of them back to dense, I'm going to switch all
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 ?
it's possible to run a standalone server with a gui, if you set headless: false in the settings file
see assets/settings.ron
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 ?
the server renders the Confirmed entity, but the clients would typically render the Predicted or Interpolated version
I see, so it's a live view of the world as seen from the server
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
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
Hi, it's because sometimes it's useful to visually inspect the server world; yeah the comment is probably incorrect
Maybe! I'm not familiar with xpbd_3d but it's possible that those plugins are required
alright, thanks!
This example uses xpbd_3d + tnua if you're interested: https://github.com/panjeet/networked_cube_test/blob/master/Cargo.toml
ah interesting, I'll check that out
(it might be a little bit out of date)
do you by any chance know about an example with rapier ?
I don't know of any, sorry
np :D
@wintry dome i added the additional settings for input delay and rollback: https://github.com/cBournhonesque/lightyear/commit/fe76cdb2d19db32e829d40ec6190508954d69a92
but i cannot test it because i need leafwing to be on 0.14-rc3 as well
(i switched to working only on the bevy-0.14 branch)
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
sounds good, thanks
here we go: https://github.com/cBournhonesque/lightyear/pull/480 and i updated https://game.metabrew.com
argh, almost. i pushed wasm that conects to localhost. one sec
fixed
nice thanks. So the future inputs are used only for rollbacks and for bullet spawning, but not for movement, right?
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
Are they actually used for movement though? because the movement will only depend on actual replicated components. (they are used during rollback though)
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)
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)
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 🤞
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
would you mind creating an issue? that's good to know
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?
thanks 😊 no reason it's commented, I think I was just enjoying seeing bullets bounce off stuff.
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)
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)
I would like to use most recent input for movement, but ignore fire inputs unless they are current
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.
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?
I guess it works, but it would probably be clearer to just check if the current tick is present in the InputBuffer
You're right, it looks weird.. I think I did that because get() isnt public
Btw what did you use to host your demo? (As in which company)
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)
Aha that's fine, I dont care about that
I thought the commits would get merged into one
yeah maybe there's a check box when you clikc merge to do a squash, it often does
You could use your hetzner server to have a web front-end, talk to edgegap and ask it to spawn game servers
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
yes that would be the way. happy to host a coordination server like that. would be nice to spin up servers as required
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
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
It's here: https://github.com/cBournhonesque/lightyear/tree/edgegap/examples/simple_box
I had trouble building it crossplatform, so I built it on github runners here: https://github.com/cBournhonesque/lightyear/blob/edgegap/.github/workflows/edgegap.yaml
(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
in general there's a lot of things that should be done to improve performance i think; reduce allocations and the number of systems. But yeah on a game like yours with 20 entities it's weird that you would get a death spiral
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.
thanks
probably worth building one docker image containing all the examples, and just launching with a different entrypoint depending on what example you want
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
I just directly ran each binary separately with supervisord
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)
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
🤔 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.
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 😅
I'm not sure I fully understood, but yes adding the LeafwingInputPlugin registers some types to the ComponentProtocol!
basically because I added it only on the client and not in the server, the protocol was no longer "in sync"
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:
- Why does the child component not replicate without the explicit ParentSync&Replicate even though the default hierarchy has recursive:true?
- 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 ?
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
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
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)
yes I'm aware, that was not well explained mb
Are you doing PrePrediction? Does the server replicate back to the original client and then gets authority on the entity?
yes it's using preprediction indeed
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
to be clear, the player bundle adds PrePredicted::default() and client::Replicate { group: REPLICATION_GROUP, ..default() }
yes, will do, thanks!
Otherwise I would appreciate it if you could create an issue 🙂 (with the server and client code used for replication)
I'll make one shortly!
what's the best way to convert a ClientId to that player's controlled entity, on client and server?
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
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.
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?
@royal saddle I fixed the issue in the cb/0.16 branch which is on bevy 0.14: https://github.com/cBournhonesque/lightyear/pull/493
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 usingNetworkTarget::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?
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 withnative - 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
tbh anyone thinking about having networked prediction is probably using leafwing
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
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 ?
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
I'm not entirely sure if it would work or not in the 15.1 branch, but in general input-handling has been greatly simplified/improved in the main branch so I wouldn't be surprised if it doesn't work properly in 15.1
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
yes, only the cb/0.16 is on bevy 14. All my new changes are added to that branch directly
when you say new release, you mean the upcoming one ?
yes
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
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)
ah ok, I wasn't sure if it actually did in the examples, but it's something that was causing the shared_movement to not run on the predicted remote entity
Maybe this change https://github.com/cBournhonesque/lightyear/commit/021c5b8e0c451cb970eb9dcd5017e90d401fa88a#diff-cc01daa248fdc1a1aeac2f1e77058ba4959da787f1375a0473f251aeefaf92d8L42 messed up the example? @wintry dome
when I add it manually it fixes some things yes
ok got it working with the main branch!
awesome
@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
maybe take a look at the leafwing examples directly: https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/examples/mouse_position.rs
Hm you might have to set the world coordinates position directly in the ActionState
oh wait i can just put anything into the action data? wasnt aware :o
uh action state* ig they call it
ah i guess just a vec2
just a vec2, like this: https://github.com/cBournhonesque/lightyear/blob/bde464a52861f764ae0d592c34761a91f4f92a19/examples/bullet_prespawn/src/client.rs#L85-L85
i guess this example should also convert that to world space coordinates prior to sending to the server
i guess for anything else other than mouse pos i'd have to use messages then
What other things to you have in mind?
just camera rotation prob
The native inputs can store anything, I guess that's an argument for keeping them
mhh
maybe a way to directly influence the input buffer would be neat too
although prob not needed in most cases
influence?
like uh, basically write inputs without depending on native / leafwing
well that's native basically. Native is just a raw input buffer provided by lightyear
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
it's just that a lot of things are broken in native I think (input delay, remote player inputs. Probably pre-prediction as well)
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
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? 😅
Do you have the link to the tnua example? I lost it aha
This is the camera system https://github.com/idanarye/bevy-tnua/blob/main/demos/src/bin/shooter_like.rs#L422
and this the movement system https://github.com/idanarye/bevy-tnua/blob/main/demos/src/character_control_systems/platformer_control_systems.rs#L18
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
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.
and in which schedule would you handle that ?
In FixedUpdate
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.
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?
i think the local address it binds to on the client? the examples create it like this:
let client_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), settings.client.client_port);
with client port being 0 too, so it's assigned automatically
but why does a client need a bound socket?
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>)
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 👍
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.
that's a nice details, thanks
Iirc wtransport requires it
its not needed on the web
@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
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
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
What does your CameraAction contain? A vec2?
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
So basically the mouse position w.r.t to the client's window?
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
i.e. the character turning to the left or to the right, like in the tnua demo?
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
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.
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)
No; I'm a bit busy now but will come back in 30min
oh np take your time
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)
Bundle that indicates how an entity should be replicated. Add this to an entity to start replicating it to the server.
So the general flow is instead:
CLIENT
- you move your mouse.
- in PreUpdate, your
ActionStateis updated based on the mouse movement. - in FixedPreUpdate, the
ActionStatefor the current tickNis stored in theInputBuffer - in FixedUpdate, you update your character's movement/rotation based on the
ActionState - in PostUpdate, an
InputMessageis sent to the server containing the last fewActionStates from the last few frames (created by reading theInputBuffer)
SERVER
- the server receives the
InputMessagecontaining theActionStatefor tickNwhen it itself is simulating tickN-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 theActionStatefor 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
ah ok that's super clear thanks
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)
hm this currently is actually added to the shape
should I just remove it, or what is a good usecase of it
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
I got it from this example https://github.com/cBournhonesque/lightyear/blob/main/examples/xpbd_physics/src/protocol.rs
Ah I see. That's fine, it's just for PrePrediction.
- The entity is spawned on the client in the predicted timeline
client::Replicateis added to the entity, so it gets replicated to the server.client::Replicateis removed from the entity right after the first message is sent, because the entity isPrePredicted(that's done internally by lightyear)- the server receives the entity and spawns it in the server world.
- You add
server::Replicateto the entity on the server; the server is now simulating the entity and has authority over it
yes the pre-prediction logic is a bit confusing maybe
I'm slowly getting the hang of this :D
well it doesn't help that I'm completely new to this field
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
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
Yea i added PrePrediction because I saw that it's used in unity: https://docs.unity3d.com/Packages/[email protected]/manual/ghost-spawning.html
It's mainly so that you can spawn an entity with 0 delay on the client (because it's spawned on the client's predicted timeline). If all you're doing is spawning your client entity once, it doesn't really do much.
yeah I definitely see the use for it
- 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
might be a good idea :D
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
but that's stuff that leafwing handles itself no ?
I don't populate the ActionState, I only consume it
Maybe; is CameraActions updated directly from your mouse motion by leafwing?
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
Can you show me your input code? Because even in the leafwing examples they update it manually: https://github.com/Leafwing-Studios/leafwing-input-manager/blob/v0.13.3/examples/mouse_position.rs#L48
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
I'm using the mouse motion example https://github.com/Leafwing-Studios/leafwing-input-manager/blob/v0.13.3/examples/mouse_motion.rs
as I'm interested in the x/y movement of the mouse, not the actual position of the mouse
also the mouse position example on the main branch is way shorter https://github.com/Leafwing-Studios/leafwing-input-manager/blob/main/examples/mouse_position.rs
anyway, it seems to work now!
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? 🙂
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.
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?
I mean the movement of other players
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
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...
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)
I didn't make any changes, I just ran the example
On main? I don't know, it's smooth for me. Is it also unsmooth for anyone else?
on main, yes. tomorrow I'll send a video of what it looks like.
Has anyone ever tested if interpolation works if we are running a local server?
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
But wouldnt it be nice if u could share rendering logic between serve / local mode?
So mocking interpolation would be neat i think?
same for prediction ofc
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
err didnt u just say interpolation doesnt run in host server mode?
so if you have logic that only renders interpolated entities then there would be nothing to render
You don't need interpolation because you are the server. Your world is simulating every entity every frame, there's nothing to interpolate.
Ah yes, this is already handled via this trick here: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/server/replication.rs#L270
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
Oh ok neat
that's how the examples manage to still work with 0 code change, even in host-server mode
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?
Hm this looks good to me! I think it's similar to what I observe locally
No, client sends inputs for every tick. The server update's the entity's position based on the client inputs, and then replicates that to all clients. Clients interpolate between the server snapshots
lightyear doesn't have client authority then?
Ah, you're asking for entities that are directly replicated from client->server. The server doesn't have any interpolation for them
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
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
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?
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)
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
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
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
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
yeah essentially, and then you just get an inconsistentency over time on which ticks align with which positions and that cause stutters
So originally the movement should be done in 5 ticks, but now it's done in 12 ticks which can make it weird
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
You mean a client C1 maintains a separate interpolation_time for each other client?
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
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
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)?
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
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();
}
}
This works for me on main
hm, time i did cargo clean and reset things then, sorry for false alarm
No worries
Hey I was reading through the examples and I noticed that they use something called a connection manager? Just wondering where it comes from
It's a resource that lets you send messages: https://docs.rs/lightyear/latest/lightyear/client/connection/struct.ConnectionManager.html#method.send_message
Wrapper that handles the connection with the server
and that generally handles a lot of the networking internal stuff
No I meant is it automatically added by a plugin or is it added by yourself?
It's automatically added by the lightyear plugin
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
Have you read through this? https://cbournhonesque.github.io/lightyear/book/tutorial/advanced_systems.html#client-prediction
It explains the difference between Predicted and Confirmed
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?
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
I think what I meant to say is
The entity on the client that I am controlling is the predicted entity
I think
Yes
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
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.

oh i was despawning the player entities; i see the same thing with the ball, hmm.
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.
this doesn't seem to run for the ball for some reason: https://github.com/cBournhonesque/lightyear/blob/deec7e62fae81cbf328728a6a08d2cc17032cbd1/lightyear/src/server/replication.rs#L334
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
looks like a potential job for observers in bevy 0.14?
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
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
really? i just found out what causes this and i would expect this change to fix it
it's because we rebuild the server on start: https://github.com/cBournhonesque/lightyear/blob/deec7e62fae81cbf328728a6a08d2cc17032cbd1/lightyear/src/server/networking.rs#L297-L297
which gets rid of some metadata necessary for replication
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
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: [] }}
no it looks like different ticks
i would expect Replicating to need to be removed and readded for Added<> to trigger a second time..
oh because we run it once in PreUpdate and once in PostUpdate
ha, yes that'll do it
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?
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 🤷
yea, maybe i should just remove dynamic server restart entirely
can't think when restarting the server should be necessary and preferable to a process restart
thanks! out ATM will give it a go later today
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,
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
@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
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?
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
Is that what I want tho?
Whats wrong with just letting the client replicate it's physics to the server?
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.
Oh so it would allow people to edit their like position and stuff I get it thank you
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
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?
seems fine.. nothing shows when you zoom in on that section?
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
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?
They should be, there is a system that keeps position in sync with transform
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
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
you can take a look at this example: https://github.com/cBournhonesque/lightyear/blob/main/examples/bullet_prespawn/src/client.rs#L77
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
This is perfect for me thank you
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,
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
I followed this exactly but my inputs for the cursor don't seem to be replicating to the server for some reason
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.
Maybe it's some ordering issue, did you make sure you also run the system in the same system set as in the example?
Oh I forgot about that I assumed ordering wouldn’t matter
Oh yeah it didn't seem to help
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?
oh looks like you can just put any length vector into it ok
@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
actually, sometimes the walls are flickering even with no rollback
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
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
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
Have fun :)
@pine cape i can only imagine the impact this had 😂
unfortunately the 3 biggest upgrades are not really usable because they don't work in edge-cases:
- replicate changes since
last_sendinstead of sincelast_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
haha, i meant instead of bitcode
:P
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.
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
@pine cape i don't see the enhanced-determinism avian feature enabled by lightyear. that might account for demo weirdness?
ah didn't know about this feature, enabled it but it didn't change much
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)
i think this feature should be avoided unless you are writing a non-authoritative networked game
it is slower than the default and should not be needed for lightyear i think
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
hm, maybe i messed up somewhere then
wanted to do it this evening
@pine cape #crates message
if u run into any issues lmk, didnt find anything during testing tho
great
@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
What do you mean; possible to do what?
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
But what is the context? there's no reference to the camera currently
in a general context, i was just thinking through how camera stuff could work with lightyear
but the server doesn't have any rendering, so it doesn't need any cameras, no?
i meant more like an abstract camera, networked culling basically. but yea in theory it would only be an aabb for culling the entities we want to replicate through interest management
if that makes sense
3d would be a bit harder
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
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
yea it should be fine
"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
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
oh wow ok didnt know is there some docs/examples?
how can the entity view the world in graphical/rendering way
I think any of the examples in the lightyear repo work. The world is replicated, you can then place a Camera on the client
Does lightyear support avian yet?
or have the feature
actually should I bother using avian or should I stick xpbd for now?
Yes the latest release support avian
Nope, still there
Hi, lightyear doesn't resend message to host when mode is Mode::HostServer? Bevy replicon has this behavior, which is really convenient
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
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.
Thanks, hope you can fix the docrs deploy as well. Building docs locally take so long 😭
they are up now! https://docs.rs/lightyear/latest/lightyear/
Lightyear
I figured out a way to do it, but it would require to change the api from send_message(&M) to send_message(M); not sure if that's acceptable..
I'm fine with that, bevy replicon also take owner ship for event so I think we will be fine
is it possible to register a bundle for replication?
Nope it's not possible currently
What would be your usecase, to have a different serialization when replicating a bundle?
resource replication relies on the ability to send messages using references
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
I managed to make it work while still keeping the current API. https://github.com/cBournhonesque/lightyear/pull/538
This PR enables sending messages from the local client to the server
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
Is there anyway to pause/resume InputMessage when using Leafwing feature? For example I dont want to send input when the game is paused.
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.
thanks
I fixed the message issue, now there should be no issues sending/receiving messages with the local client in host-server mode
seem not work on my end. Does it need any additional config?
did you upgrade to the main branch?
yep, on main right now
I tried it in the simple_box example, it seemed to work for me
I will try to create minimal reproduce tomorrow, it's late here
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
It work now, turn out that I didn't call commands.connect_client()
great
@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? 
Hm maybe have a separate component called CameraTransform? or ExponentialTransform?
Ultimately the SyncTarget https://docs.rs/lightyear/latest/lightyear/prelude/server/struct.SyncTarget.html
should probably allow overriding the interpolation function
Component that indicates which clients should predict and interpolate the entity
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.
its really just the transform thats serverside. So that i can cull the entities serverside very precisely instead of replicating everything inside a certain radius or smth. The rest is clientside.
Imo overriding makes most sense
so the client transform is replicated to the server so that the server can not replicate anything outside the camera?
nope, the server controls the cams transform according to inputs (by following the player character)
For anticheat purposes
Hey, I'm just reading through the docs to get accustomed to this before I start coding, and I found a link that doesn't work on https://cbournhonesque.github.io/lightyear/book/tutorial/build_client_server.html#clientconfig in the NetCode link text. The link is https://docs.rs/lightyear/latest/lightyear/prelude/client/enum.NetConfig.html which for some reason doesn't work
Looks like it should be https://docs.rs/lightyear/latest/lightyear/connection/client/enum.NetConfig.html instead
Actually looks like quite a few of the links to docs are outdated on that page, and presumably on other pages too
Yes it's hard to keep the book links up to date. Thanks for the report!
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
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 obvious
@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.
oup sorry bout that
nw
Ah that's perfect, thanks!
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
I knew I was missing something smh
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!
Yes you would normally add render-related components on the client only when the entity is first replicated.
You can either:
- listen to network events: https://cbournhonesque.github.io/lightyear/book/tutorial/basic_systems.html#network-events (for example
ComponentInsertEvent<Player>) - all replicated entities have the
Replicatedcomponent; so you could also query forQuery<Entity, (With<Replicated>, With<Player>, Without<Mesh>)>
Exactly what I needed, thanks!
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
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!
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
thanks! i'll try to take a look in the coming days
*only if u dont actually need the location for aiming, eg for non-linear missile paths
@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
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)
does lightyear support sending messages through steam's matchmaking lobbies or should I use steamworks-rs directly for that? (https://partner.steamgames.com/doc/features/multiplayer/matchmaking#7)
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
okay cool, thanks
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)
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
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
you should be able to uncomment the steam-related parts in https://github.com/cBournhonesque/lightyear/blob/main/examples/lobby/assets/settings.ron#L23 to try via steam
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
Host-server mode (client and server in the same process) should be possible with steam backend I believe
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?
Why not have each asteroid in a separate group 🤔
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
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
did u try to make the asteroids interpolate only tho? Collisions shouldnt necessarily be janky just because you interpolate i think
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)
hm weird, i never had that issue even tho i have that setup
it'd be like trying to kick a ball, except you're looking through goggles that cause a 100ms delay in what you see
Might be the frequency of simulation / replication idk
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
actually cant we make the replication rate dependant on movement speed and avians sleeping functionality? Maybe that could help
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
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
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
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
but they have the Predicted component?
they are snapping into position into a past position (the server position), without any prediction?
hm, and sometimes when i start my server+client i don't see the asteroids on the client, something not fully replicating
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