#lightyear
1 messages · Page 7 of 1
the matchmaker and other bits needs the env file, gameserver needs envs set in the edgegap settings
Thanks for guiding
Yeah. It’s the only thing holding me back from a successful bevy deployment rn to edgegap. But it will give a good foundation for testing locally and in production
Will definitely try this
Does anyone have an example of changing the client transport once the bevy app is running (i.e. clicking a button etc to connect through steam vs local vs udp)?
dont have an example, but you'll need to get the ResMut<ClientConfig> i think, then replace or change the net field with a new NetConfig, per the steam example, or udp etc
The configuration object that lets you create a ClientPlugin with the desired settings.
hm pretty sure you would need to disconnect the old transport first
yeah step 1 is probably be disconnected.. make the change to clientconfig, then connect
never tried it myself
should work given that connect() basically rebuilds everything needed
Hi I need help on hierarchy replication. How does the client recieve hierarchy replication, what special components do they have? and will they still be in a hierarchy? thank you :D
This example does what you want https://github.com/cBournhonesque/lightyear/tree/main/examples%2Flobby. As others said you need be disconnect and reconnect for any transport changes to be applied
HierarchyReplication should be enabled by default. There is an internal component ParentSync used to sync parent/child relationships
You can see more here https://github.com/cBournhonesque/lightyear/blob/main/lightyear%2Fsrc%2Fshared%2Freplication%2Fhierarchy.rs
I see, do they still have the ParentSync on the client side?
Yes
@pine cape I'm targeting bevy_remote so yeah just getting it ready for 0.15 via the RC1. Seems like you have it under control.
We can compare notes once you land next week or whenever
If you already have some changes made, feel free to start a PR! It would be helpful for me as a starting point
It will be a bit rough as there's no avian/leafwing/parry forks to refer to ATM. I will work something out with those and PR
Just the lightyear changes is probably fine, for those it's probably better to just wait
Thanks a lot btw
Agreed
No, thank you for all of your hard work on the crate. It's been a pleasure to use
I'm cooking up some editor/blueprint-esque stuff against this all. Looking forward to publishing that all but first things first with the 0.15
Question about this specific component in avian_2d example, Position. Where and how he gets updated? I cant seen to find it is like he magically just updates
Is he from lightyear itself? The imports are a little confusing
Position is from avian unless you are talking about something else
Well it seens like rapier doesnt have that global rigidbody position.
It just directly apply to transform them global transform
oh boio this gonna be hard
pub(crate) fn replicate_inputs(
mut connection: ResMut<ConnectionManager>,
mut input_events: ResMut<Events<MessageEvent<InputMessage<CharacterAction>>>>,
) {
for mut event in input_events.drain() {
let client_id = *event.context();
// Rebroadcast the input to other clients.
connection
.send_message_to_target::<InputChannel, _>(
&mut event.message,
NetworkTarget::AllExceptSingle(client_id),
)
.unwrap()
}
}``` In the avian 3d example there is the replicate inputs to all other clients, I guess that is due to avian 2d example. But alas the entities in avian 3d are interpolated always not pre predicted like in 2d. Anyone know why is this here?
Probably just a mistake
Avian 2d i think it is broken, gonna open a pr to slight fix both
What is the issue with avian 2d?
It seens to be not be able to take input
In separate client and server mode
Might be something on my end tho
sorry for the random question , I just don't know what I'm missing here 🥲
Resource requested by our_server::replicate_inputs does not exist: bevy_ecs::event::Events<lightyear::shared::events::components::MessageEvent<lightyear::inputs::native::input_buffer::InputMessage<shared_networking::PlayerActions>, lightyear::connection::id::ClientId>>
I added app.add_plugins(LeafwingInputPlugin::<PlayerActions>::default()); to the app, and the leafwing feature, is there something else necessary for this?
Nvm, I just imported the wrong type for InputMessage 🥲 . Sometimes I feel like a moron.
I havent managed to get it working with a nat yet. do you know when you get time writing a full tutorial for getting it running? i would appriciate it alot 🙂
I'll be back at it next week, when my kids are back at school 💪
oh god i finally manage to fully migrate my physics
Who did the spaceships example, deserves a godamm oscar
You mean apply the force only on entities predicted?
Ya
and the ones on the server
@pine cape Mr Peri, mutating the replicate components such as sync target and such will not adjust the type of replication? Like for example let say I change synctarget to one to all that wont automatically spawn predicted entities in all my clients if replication is default correct? I should use rooms correct?
Mutating the components should work for some of them, for example the replication target. I'm not sure if it works for the sync target
I think it doesnt as a player join my lobby he adjusts the server player synctarget to predict in both him and the new guy client id. But alas he only spawns a replicated entity
I wonder what might cause that
fn listener_join_lobby(
mut events: EventReader<MessageEvent<EnterLobby>>,
mut lobbies: ResMut<Lobbies>,
mut room_manager: ResMut<RoomManager>,
player_entity_map: Res<PlayerEntityMap>,
mut online_state: Query<&mut PlayerStateConnection>,
mut connection_manager: ResMut<ConnectionManager>,
mut commands: Commands,
) {
for event in events.read() {
let client_id = event.context();
let lobby_id = lobbies.lobbies[0].lobby_id;
info!("Inserted player {} unto lobby {}", client_id, lobby_id);
lobbies.lobbies[0].players.push(*client_id);
info!(
"Added physics component in server for client id {}",
client_id
);
add_physics_to_server_player(
*client_id,
&player_entity_map,
&mut online_state,
&mut commands,
);
}
}```
First he does this to the client that told to enter lobby
Them he does this for all client inside lobby[0]
for client in lobbies.lobbies[0].players.iter() {
if let Some(player) = player_entity_map.0.get(client) {
info!("Inserted player unto room manager {}", player);
// FOR NOW WE ONLY HAVE ONE ROOM
let lobby_id = lobbies.lobbies[0].lobby_id.clone();
room_manager.add_client(*client, RoomId(lobby_id));
room_manager.add_entity(*player, RoomId(lobby_id));
info!("Defining type of replicatinon for that player important to know he is from replication_group 1");
let replicate = Replicate {
sync: SyncTarget {
prediction: NetworkTarget::Only(lobbies.lobbies[0].players.clone()),
..default()
},
controlled_by: ControlledBy {
target: NetworkTarget::Single(*client_id),
..default()
},
group: REPLICATION_GROUP,
..default()
};
commands.entity(*player).insert(replicate);
}
}
Overiding replicate value to follow new logic
I think my question is, how does one. Tell server to start predicting new entities inside a lobby
So the entity should be predicted by all players, not just one?
So the ReplicationTarget change takes effect (i.e. the entity gets replicated to the new player), but not the SyncTarget change?
Exactly
All player is lobby should predict him
My ideas was, first spawn the guy with a simple predict all. But when entering a room mutate his replicate so only players in that room should predict him
Here is a video of my agony
Yes I think the way prediction/interpolation is design right now is a bit flimsy. It's purely a client concern but right now the setting is defined on the server which is a bit strange
does lightyear support custom IO layers?
Yes, anything that supports those 2 traits: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/transport/mod.rs#L84
What do you have in mind?
Ah; otherwise one level up the stack, I have a ServerConnection resource: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/connection/server.rs#L175
I wanted to switch to having one entity per connection, where the ServerConnection is a component
but haven't had the time for that
also I'm waiting for relations to ship to make this kind of stuff easier
what's your use case for relationships here?
To be able to query stuff like Connected<ClientId> where maybe each ClientId would be a separate component
haven't thought it through to much
ah right, you have a separate ClientId
Any ideas on how I can save it Mr Peri?
That is true it can cause a little bit of confusion
Hm the logic here would have to change: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/server/replication.rs#L600
Right now i'm only updating replication if:
- replication target gets updated
- a new client gets connected
Maybe try updating the replication-target to only include the players in the lobby? In which case the logic in this function (which also handles prediction/interpolation) would apply.
Are you using rooms or ReplicationTarget::All currently?
I will try my dandiest as this is my first direct lightyear pr, please evaluate
I meant that you could try in your code to update the replication target, you wouldn't need to make a pr to lightyear
Hey I'm getting some compile errors related to leafwing?
I haven't used rust in awhile not sure what all of this is
@pine cape sorry, haven't been able to make a repo for the rollback stuff yet
been busy with contract work
most probably pattern matching not fully covered
u need to make sure all of the enums are convered
is that not an error happening in the internals of the packages?
I thought it was
cause it doesn't happen while compiling my code its while compiling lightyear
oh looks like it is...
are u using a leafwing version that is different from lightyear?
oh sorry tired me sometime hallucinates
So what you are telling me is to change replication target from all to only lobby players? That oughta sync it
Oh my gawd
It worked
I sometimes ask myself if you are a wizard
But wouldnt it make sense to update whenever synctarge is changed?
this is for u
i'm writing docs for deploying bevygap - having to do a bit of extra work to support self-signed NATS server certs. got some unpublished changes coming up for that (so ENV var names changed etc in the Bevygap NATS Setup section). but the "Nats server setup" might be useful already https://www.metabrew.com/bevygap-book/installation/nats.html
@dreamy silo ^
i'm also in the middle of changing bevygap client plugin to use a websocket to talk to the matchmaker
so these docs aren't in sync with main atm
Is this kinda of like a setup for actual ingame server?
Like it scales up and down
According to user amount?
it's a system to automatically deploy servers in response to player demand, around the world
using Edgegap
so the flow is the game client clicks connect, the matchmaker waits while edgegap starts a new server near your players or finds an existing on, and then you pass back the server details to the client so it can make the lightyear connection
full tutorial pending..
Thank you so much for this.
Definitely helpful
Will try setup later tonight or tomorrow
cool, see if you can get to the point where nats-cli is connecting. the bevygap nats bits in the book aren't in sync with main atm
Try running cargo update and maybe use the main branch of lightyear?
amazing!
hm, yea it's just a stopgap solution
Manage to make it work but hell am it is not pretty
hahahahaha
First need to set to none replication target
them change as if it is the same client it does not consider as changed
MR PERI look physics with lobby yay
NOW animations
and gravity that doesnt break everything
very nice!
I am using the main branch and I just cargo updated still doesn't seem to work
I'm not sure what I'm doing
[workspace.dependencies]
bevy = "0.14"
bevy_egui = "0.30"
bevy-inspector-egui = "0.27"
serde = {version = "1.0.188", features = [ "derive" ]}
bincode = "1.3.3"
avian2d = {version = "0.1.1", features = ["serialize"]}
lightyear = {version = "0.17" , features = [ "leafwing", "avian2d" ] }
leafwing-input-manager = "0.15"
this uses the version 0.17 version of lightyear
you should use something like lightyear = { git = "../path/to/git/repo", branch = "main" }
yes
I'm sorry for bothering you again but I'm getting another error
resource does not exist: lightyear::protocol::component::ComponentRegistry```
I did take a small dive tonight. Good guide so far. I also took another look at synadia cloud’s option and that was definitely alot easier to setup in terms of stuff needed for the nats cli context. JWT based, just had to give it a .creds file. Their free tier would be nice to use now during development. I just need to tweak your crate a lil and add a optional nats async connection option using creds or creds file in bevy_shared
it's hard to tell what's going on just from that error. The resource should exist, maybe you're using multiple versions of a same package?
thread 'main' panicked at C:\Users\poopn\.cargo\registry\src\index.crates.io-6f17d22bba15001f\bevy_ecs-0.14.2\src\world\mod.rs:1867:32:
resource does not exist: lightyear::protocol::component::ComponentRegistry
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14\library/std\src\panicking.rs:662
1: core::panicking::panic_fmt
at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14\library/core\src\panicking.rs:74
2: bevy_ecs::world::World::resource_scope<lightyear::protocol::component::ComponentRegistry,tuple$<>,lightyear::protocol::component::impl$4::register_component::closure_env$0<common::player::PlayerId> >
at C:\Users\poopn\.cargo\registry\src\index.crates.io-6f17d22bba15001f\bevy_ecs-0.14.2\src\world\mod.rs:1867
3: lightyear::protocol::component::impl$4::register_component
at C:\Users\poopn\.cargo\git\checkouts\lightyear-2cfb5e6660946fe3\12b95e7\lightyear\src\protocol\component.rs:1066
4: common::player::register
at .\common\src\player.rs:16
5: common::register
at .\common\src\lib.rs:48
6: server::main
at .\server\src\main.rs:34
7: core::ops::function::FnOnce::call_once
at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14\library\core\src\ops\function.rs:250
uhh so whats the clean command for rust packages again?
oh it's just cargo clean
mb
is there a new way of setting up lightyear now?
no, nothing changed
sounds good, yeah a new env var to use JWT creds should be good
I cleaned it out completely and nothings changed
So I have the following issue with leafing input, I already have a predicted client spawned and aligned with in my server. And it tries to reproduce it is input message to my current client, even tho he doesnt even have a replicated confirmed entity. Any ideas on what might be causing this?
2024-11-06T10:27:39.002115Z ERROR lightyear::client::input::leafwing: received remote player input message for unrecognized entity The error message
Oh found the reason.
@wintry dome Hey Rj might I ask you something, in the spaceships example you add input staleness. What happens in live games, if you dont do that? Stutter teleportin and such?
it's so you can decide how many frames ahead you want to predict inputs from remote players. if your prediction is wrong, your client does a rollback to reposition players, which will look like a stutter in some cases
perhaps you think it's ok to assume same inputs as previous frame, for up to 5 frames, after that you assume no inputs. pretty safe to just ignore entirely to begin with i think, bit of a niche case
I tought lightyear had that imbued, then I noticed your comment
// config.prediction.maximum_input_delay_before_prediction = settings.input_delay_ticks;```
i think lightyear may have changed those settings since i wrote spacehsips code, not sure though
Turns out it actually fixed my teleporting so thanks for doing it
Only a fixed input delay currently works, the other input-config settings cause some issues
just curious, what was the reason for this error?
I replicated inputs to entities in server that should be predicted by client
But client wasnt yet replicating
So basically i needed to set replication to only lobbie
Btw you were the guy who had weird telportatikb going on
I found out that constant collisions caused a lot of jittering
And flickering
No idea why
Nah that wasn't me, I'm not even that far with my thing yet lol
Ah interesting, that same error is happening in the avian 2d example, maybe I'll try creating a PR for it
Anyone have an example of how to use tnua-controller with lightyear and avian? The main issue I'm having is: the host player's movement is choppy for the non-host clients, while the clients' movements are fine on the host server's game. I'm wondering which components I need to replicate to the clients so they can properly interpolate the movement of the other players
Try replicating GlobalTransform, i think that's what tnua uses under the hood
Just globaltransform
i guess I should be replicating Transform too? seems like just replicating GlobalTransform, it gets out of sync with Transform on the Predicted entity on the client. I guess since GlobalTransform is built from Transform each frame, not the other way around
looks like tnua is just updating avian's LinearVelocity and AngularVelocity every frame, maybe it's enough to replicate those plus Position/Rotation. I will play around with that
god i have an stability issue that i have no idea what causes it
So annoying
man i swear to god everytime i cry in here i find the reason why it is amazing
You need to replicate all the components that let you do prediction on the client. I think tnua uses GlobalTransform internally at some point. I remember an example using tnua where all the issues disappeared once GLobalTransform was replicated
is this the example you mentioned? https://github.com/panjeet/networked_cube_test/blob/master/src/protocol.rs. Looks like they commented out their GlobalTransform replication at some point, with the commit message:
try only syncing transform, and sync linearvelocity to the current player
https://github.com/panjeet/networked_cube_test/commit/7b43891cf45fc0d5fd51ee84f730ca58da65bc4d#diff-d8c31d407e43532e4152070bdaf92d7fff58c81b39b4308cad7c769519d31376. who knows which approached worked better 😆
yes that was the one! Maybe try both then 😛
will do. appreciate the help!
Is there any video tutorial for lightyear? The lightyear book example ain't that great to follow.
there are quite a few different examples in the repo under examples/ perhaps one of those makes more sense?
They are all based on the same starter example.
You know what I think i am gonna do a cris and make a tutorial video on lightyear
Would be lovely
But I can give you a short summary
Might just also be, because i'm trying to split the code out more. Like separate app directory for server and client
Wouldnt do that I made the same mistake
Is better to follow the common examples crate
IF you are a newbee to network games
i have a version of the spaceships example split into a dedicated server (no gui) and client, and shared. if you build without default features per the readme i think the bevygap bits get out of the way. https://github.com/RJ/bevygap-spaceships it only supports webtransport.
@wintry dome nice i'll take a look at that. Yeah I'm trying to go more towards a microservice setup with event driven message bus design and such
The use of nats is intresting. It's a combo of redis and kafka as far as i see
yeah, you need comms between the game servers and the matchmaker bits, and need a bit of key/val storage for config, so it made sense.
maybe i should have used redis, it's easier to deploy 🙂
uhu i was thinking to use kafka, dragonfly and scylladb.
i'm a infra kind of guy 😄
for me it's more to learn about a high scaling infra for something like a game. Not the game itself 🧐
it's pretty easy to spin up such a thing in docker together with prometheus/grafana for data gathering
I realize good infrastructure is incredibly important in multiplayer games, especially MMOs
yeah. Also if you can split off the lobby manager. You can spin up new containers per lobby for example
that's why i like the prometheus support in bevy. Means you can monitor everything and scale on the load info of that for example
Could you make containers per map/world too? And players could jump between them?
in theory yes. That's sort of how they do it. Don't know how far lightyear supports it
I'm looking at the Transport trait now, what are the guarantees I have to uphold for the packet sending and receiving? And how are the SocketAddrs used? (Especially on platforms like steam which don't use socketaddrs directly to identify clients)
My end goal is that network connections are represented as entities with the Session component in aeronet, and I want packet sending/receiving to go through Session::send: Vec<Bytes> and Session::recv: Vec<Bytes>. Just wanted to ask what the general process would be for enabling this. Probably would involve an mpsc/spsc somewhere?
Transport is to send raw bytes to a socketaddr, and then you have the ClientConnection and ServerConnection traits to build connections.
Connections don't have to reuse the transport logic if the connection already has a way to pass bytes
For example for steam
I don't think having connections be entities is possible in the current design, you would have to make a PR to change how lightyear stores the ClientConnection and ServerConnection (which are in a resource currently)
that was based on Periwink’s suggestion at the time. Tnua does some weird stuff with the way it synchronize between the transforms. I spent like a week trying to fix it, debugging system ordering and could never get it perfected. Im not sure if its better with avian or whether the sync issues have gone away with subsequent lightyear fixes
Avian 3d example is also extremely vulnerable to breakage and sync issue
@pine cape mind if I refactor it adding forces like gravity and such?
Elements when ststionary try to correct even tho they are perfectly aligned in server
I don't mean changing lightyear to have connections as entities, I mean the connection could point to a single Session connection entity somehow? I'm not entirely sure how this would work, but maybe the Transport trait isn't what I'm looking for and I need something higher level
pub enum ServerConnection {
Netcode(Server),
Steam(Server),
}
looks like this might not be possible, since the connection is hardcoded 😕
I've got working decently by replicating just Position, Rotation, LinearVelocity, and AngularVelocity. I am predicting Position and Rotation to all clients, and using visual interpolation: https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html
The client's controlled entity is a little bit jittery still so not perfect, but definitely working a lot better than before. Still gotta fine tune it
Would it be possible to serve multiple worlds on the same light year server, just different ports?
yeah the jitter is a problem I got stuck on
I just ran into https://github.com/cBournhonesque/lightyear/issues/684 when trying to use WebTransport on, well, the web 😄
Does anyone have suggestions for how to work around it?
I was able to reproduce the issue with the first-party examples. I have offered a PR with an admittedly blunt fix for consideration: https://github.com/cBournhonesque/lightyear/pull/687
@boreal inlet i've run into the "ring breaks wasm builds" issue a couple of times, trying to remember how i solved it. i've also run into a gnarly silent linker failure that only causes issues at runtime (a js issue), which is fixed by using clang instead of gnu cc, at least on my machines. i build wasm with these flags:
TARGET_CC=/usr/bin/clang RUSTFLAGS=--cfg=web_sys_unstable_apis CARGO_BUILD_TARGET=wasm32-unknown-unknown cargo build etc..
i skimmed your pr to remove ring, and that seems helpful.
Avian physics example is broken the cubes dont movee
@wintry dome Rj I dont know if you have a better understanding than me when it comes to the visual interpolate. But from what I understand he should be attached to the visual entities that have position and rotation correct?
In my case scenario I have child entities that move based on transform, but well they are based on my rigidbody postion
Should I attach that component to them or nah?
ha.. yeah you're right, I put it on the predicted entities that get rendered, for position and rotation (see spaceships example)
looks smooth 😎
actually not sure about child entities.. the interpolate stuff only acts on eg position and rotation, then that gets synced to transform, then it propagates down to children. I guess rendering happens after that so it all works out
Yeah avian physics set, is positioned before transform propagate independently of wether syncplugin is in post update or not. Therefore the smoothed out position interpolation occurs before propagate
Wait, should a camera that follows be on visual interpolation?
No, it should follow transform normally, but it need to be set after physicsset sync, and before transform propagate
You can also try to make it follow the vec3 from position as that also would be interpolated
BUT
when rollback or lag spikes occur camera would teleport
@wintry dome Do you know what this guy sets? max_prediction_ticks: 100? I dont find him anywhere in config and in docs
Yeah this I’ve fixed
hm, likely an old setting that isn't used by lightyear any more i expect
I've been struggling with a jittering camera on the client (for a FPS style camera). can the camera be a child of the Predicted entity with VisualInterpolateStatus<Position>? Or would the timing of the camera's GlobalTransform being synced not work? I have SyncPlugin running in PostUpdate
It is not ideal, because predicted entity transform will suffer corrections, although child visual would be smoothed out. The moment you have a lag spike, boom teleports and probable jitter
I would say you need to put your camera slightly upfront of your predicted entity
And follow predicted entity transform. With an offset that keeps player camera slightly ahead of model head.
I see, the camera transform can be set after the physics and correction has finished
im actually just not spawning a player model for the controlled player right now, so you won't see a head or anything in the way
hold up i have code for you, that way you can just follow along
here is a resutl of a quick change in my camera, warning my game is 3d and it is just a display of how it works
nice!
pub fn sync_player_camera(
player_q: Query<&Transform, (With<Predicted>, With<Controlled>)>,
mut cam_q: Query<(&mut CamInfo, &mut Transform), Without<Predicted>>,
) {
if let Ok(player_transform) = player_q.get_single() {
let (cam, mut cam_transform) = cam_q.get_single_mut().expect("Camera to exist");
let rotation_matrix = Mat3::from_quat(cam_transform.rotation);
// Offset actually
let offset = rotation_matrix.mul_vec3(Vec3::new(0.0, 0.5, cam.zoom.radius));
// Update the camera translation
cam_transform.translation = offset + player_transform.translation;
}
}
I just separated my camera from the player, just observing the scene from above now, and I noticed the jittering on the player is still there, so there must be some other correction issue unrelated to the camera I must solve 🙃
i'll give this a try too!
what does your Replicate config look like?
I've got this:
Replicate {
sync: SyncTarget {
prediction: NetworkTarget::All,
..default()
},
controlled_by: ControlledBy {
target: NetworkTarget::Single(client_id),
..default()
},
hierarchy: ReplicateHierarchy { recursive: false },
group: REPLICATION_GROUP,
..default()
}
Oh yeah zoom i think zoom needs to be upfront so probably negative
pretty much equal without hierarchy
are you using bevy_tnua for movement?
I heard that might be an issue, seems difficult to get it to work with lightyear
No wouldnt dare use it for me it is api is a little too confusing
ah okay
But if you are predicting all you should predict your entity input
that's just with
app.add_plugins(LeafwingInputPlugin::<PlayerAction>::default());
right?
No your server need to replicate it back to your client
Is like hey me client am being predicted take my input -> takes inputs -> okay here is your input by me so we can sray aligned server
Oh I see. right now just Position is being sent back to the client, not the server's account of the inputs, so it just teleports?
LeafwingInputPlugin does include
app.register_component::<ActionState<A>>(ChannelDirection::ClientToServer);
but I guess that doesn't replicate back to the client? I am a bit unsure about how this works tbh
this example doesn't seem to have anything other than just that plugin for inputs: https://github.com/cBournhonesque/lightyear/blob/main/examples/avian_3d_character/src/protocol.rs
No that just debugs
Exactly
is there any way to run lightyear servers on arm? or a way to disable steamworks because that is creating issues when trying to host a server from the examples on my arm server
there's a steam feature on the lightyear crate, maybe that's being enabled by something?
i think the examples in the repo enable all the transports by default including steam
ill try to port the example over to a new rust project then and see if that works, thank you!
i ported the spaceships example to a standalone project, and i build a headless server with a Dockerfile. might be worth a look - disable default features / delete the bevygap bits to get it working standalone https://github.com/RJ/bevygap-spaceships
(it just uses webtransport)
Hey, I'm just setting up a HostServer, and it seems to work pretty seamlessly with the existing code I have for networked clients, but I was wondering what happens when I set things like prediction for local players. Should I make sure that I don't set that, or will lightyear realize that local clients shouldn't be predicted even though I requested it?
Thanks for that tip @wintry dome . I'll record the note about clang vs gcc in the related issue for posterity.
If ring is really only used for that one utility function (all it does is some light string decoding) it's probably for the best to remove it as a dependency anyway. It's a relatively complicated package with a lot going on internally, including a lot of non-Rust software.
Has anyone here even ran into the issue where if server replication send interval is low about 10 ms, we get constant rollbacks? \
I think only mr peri can solve that one
is it possible that ClientTransport and ServerTransport could be turned into traits?
Correct, the local client won't have any prediction or interpolation!
Can this be reproduced in the examples?
Oh you are back mr peri thank the lord
Awesome, thank you so much! Loving the different types of clients, it seems like a genius way of solving the issue!
So I have the following struct
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Component, Reflect)]
pub struct SunPosition(pub Quat);
impl SunPosition {
/// Spherically interpolates between two `SunPosition`s by `t`, where `t` is a factor between 0.0 and 1.0.
/// `t = 0.0` will yield `self`, and `t = 1.0` will yield `target`.
pub fn slerp(&self, target: &SunPosition, t: f32) -> SunPosition {
SunPosition(self.0.slerp(target.0, t))
}
pub fn default() -> SunPosition {
return SunPosition(Quat::from_rotation_x(-std::f32::consts::PI / 4.));
}
}```
In protocol
```app.register_component::<SunPosition>(ChannelDirection::ServerToClient)
.add_interpolation(ComponentSyncMode::Full)
.add_interpolation_fn(SunPosition::slerp)
.add_correction_fn(SunPosition::slerp);```
I move them in server like this
fn tick_sun_cycle(
mut cycle_time: ResMut<CycleTimer>,
time: Res<Time>,
mut suns: Query<(Entity, &mut SunPosition), With<SunMarker>>,
) {
cycle_time.0.tick(time.delta());
for (sun, mut sun_position) in suns.iter_mut() {
if cycle_time.0.finished() {
let t = time.elapsed_seconds_wrapped() / 2.0;
info!("Sun should move");
sun_position.0 = Quat::from_rotation_x(-t);
}
}
}```
Interestingly enough it feels like he is not interpolating to new sun position just going straight for it? Any ideas why is it because of my alpha
this looks like deterministic based on the elapsed time or ticks - can you do the calculation in systems on both server and client based on time? no need to constantly replicate the sun position data from the server.
Resource requested by <ProjectName>::shared::shared_plugin::handle_hit_event does not exist: lightyear::server::connection::ConnectionManager
use lightyear::prelude::*; should get it right?
I split my .rs files into folder in src/, tying them together using pub use and mod.rs file in each folder. how do I make sure this resource is available across these folders? shared, client, server.
also struggling to read events in src/server/server_plugin.rs that is written in src/shared/shared_plugin.rs. not sure what thats all about. event has been added in shared_plugin.rs using app.add_event::<BulletHitEvent>();...and in server_plugin.rs fn handle_hit_event system is on .run_if(on_event::<BulletHitEvent>() after system process_collisions which should be working right? this exact logic is almost identical to the one found in the spaceships example.
that error reads that the resource wasn't inserted yet when the system tried to run. so perhaps lightyear setup on the server hasn't happened yet.
don't quite follow the question, but in my stand alone version of spaceships i use separarte crates for server, client, shared: https://github.com/RJ/bevygap-spaceships
Actually the game which is working with lightyear runs just fine until i do something that triggers a system that requests connection manager. So stuff like connecting and handling new players has no issues. kinda strange if that’s all baked in and it’s not existing where I’m now requesting it. Not sure where/which part lightyear inserts the resource.
I’ll probably have to do some digging.
ok so connectionmanager only lives on the server. cant run it anywhere else thats understood.
BulletHitEvent triggering on_event and being consumed by handle_hit_event fine on client plugin but not server plugin. exact same implementation.
following this implementation: https://github.com/search?q=repo%3AcBournhonesque%2Flightyear BulletHitEvent&type=code
know other events work on server because it handles connections fine. which are triggered on connection events. very strange. bleh
@pine cape maybe it would make sense to use the existing easing capabilities of bevy_math for interpolation instead for 0.15? 
Heyy, I'm trying send over a list of entities to the server (to update their targets), but I can't find a way to convert the client local Entity ids to the server ones, do I have to hand roll it?
I expected each replicated entity to have a component that is their Server Entity, but I can't find anything like that. I found lots of EntityMaps, but I can't figure out how to make use of any of them
Their network targets?
Nono, they're minions that move towards a target, and that's what I'm updating, just wanted to give a tiny bit of context, but I think I made it more confusing 😋
Does lightyear help with replication of events?
Like other than network related ones
Yes that's a good idea!
How are you sending the entities? Did you implement the MapEntities trait on your message or component? If so the conversion from client entity to server entity is done automatically!
It's not handled automatically for now, you would have to create your own Message
Then I’m not sure how BulletHitEvent is actually reaching server and every other client. Works when I run the spaceships example. Doesn’t work when I try to implement it in my own code exactly the same way. Tried reading the reader locally in the same file it’s being sent (shared.rs) and that works fine. Also works fine in client code (client.rs). Anything I’m overlooking? or any clever way I can debug this? @wintry dome
Shared event sender:
Client receiver (works both here and in my code):
Server receiver (not working in my code, but works fine here):
event struct (works both here and on my end):
A networking library to make multiplayer games for the Bevy game engine - cBournhonesque/lightyear
A networking library to make multiplayer games for the Bevy game engine - cBournhonesque/lightyear
A networking library to make multiplayer games for the Bevy game engine - cBournhonesque/lightyear
Ahh that's whta I'm missing, I kept searching for "EntityMap", but didn't try "MapEntities" 😅 Thank you!
in the spaceships example, shared::process_collisions runs on both server and client, emitting BulletHitEvent. then separate systems for client / server handle that event. so the event isn't transmitted over the network at all.
if you do an info! log at the point where shared::process_collisions emits the event, do you see that on both client and server? you should do
iirc the server uses that event to update the scores, and clients use it to show explosion effects
make sure you've only defined BulletHitEvent once and both client and server code are referring to the same struct
are you carving up spaceships into a new split server/client arrangement? i'll have a look if you've got a github link or something
👍 made this for now https://github.com/cBournhonesque/lightyear/issues/691
Question I made a sun orbit timer, that is constantly being ticked by server should I make it so he has a unique channel for it. Would that make his ticks more continously? Avoiding slight time desyncs?
Correct I’ve split them. Lemme add you to my repo and you can take a look. Info only goes out on clients. So not sure exactly what’s going on. Beat me silly if there’s a silly mistake. Added, invite should pop in now
searching for BulletHitEvent in your repo, can't see the "process_collisions" system that writes those events.
some system should be reading avian physics collisions and using a EventWriter<BulletHitEvent> to emit those events
in spaceships, process_collisions is in the shared plugin, so runs on server and clients.
heading out for bit, back in a couple of hours
It’s in shared/shared_plugin.rs line 372
You could have it based on current server tick which the client already syncs.
otherwise yes, having an ordered unreliable channel for that would make sense imo
ah yeah, github search was not showing everything. taking a look
@viva what's the easiest way to cause a collision? i just see my ship on the client
I did that
I made a resource that is ticked by server
replicate it to client, client moves according to resource timer
Thanks for the tip RJ avoiding the structuring in server spared me some time
your server_plugin is calling app.add_event::<BulletHitEvent>(), and so is the shared plugin. client_plugin doesn't.
Right. I did that for testing.
server plugin shouldn't call add_event, just shared. dunno if that matters
also, although commented out, be careful about your debug_events, if you consume the EventReader<> in an Update system, the events will be gone before a FixedUpdate system runs
stick to only reading the events in FixedUpdate
Agreed
what's the easiest way to cause a collision so i can test
Run server, then two clients c0 and c1
Then shoot the other using arrows and space
From either client doesn’t matter
i noticed that you are only adding the PhysicsPlugin in the client, not the server. that's got the systems that apply movement and spawn bullets
ok let me explain what i actually meant:
lightyear automatically tells the client the servers tick, now think of the server tick as a "seed" for the timer, if you then have the client use the same seed for its local timer you dont need to send anything (other than the server tick which the server automatically syncs).
ofc its a minor optimization at best but in prod we want to minimize bandwidth as much as possible
actually offset is prob better word than seed
I have no idea how to get lightyear timer
i expect the problem is the server isn't running a system that it should be after you split things up
iirc its this one https://docs.rs/lightyear/latest/lightyear/shared/tick_manager/struct.TickManager.html
Manages the tick for the host system. Ticks are incremented by one every time the bevy::prelude::FixedUpdate schedule runs
oh wow..
i'm not convinced the server is even spawning the bullet. add some logging to the code that processes player inputs and make sure that a bullet spawn happens on the server.
probably the fact that player inputs are rebroadcast means it looks like it's working on the other client, but the server isn't moving or spawning bullets
thanks for the pointer
avian3d not working properly in server prob
its debugging the colliders tho
the ships are just small dots inside the box on server
but is the server spawning bullets in response to keypresses on clients?
good question. theyre spawning on both clients at least
player inputs are replicated, which is separate from whether the srver does it
yeah good pointers ill make sure bullets are actually spawned on server too. but not sure if relevant to the hitevent itself? since I am confirming that the event is being written to EventWriter with the event struct.
will debug some more later tn
thanks
@wintry dome Do you know of a way of cuptaring failure to connect?
Need to adapt my ui when occurs
I think youre exactly right. I dont see bullet replication on server at all. will fix that. thank you!
iirc there's a client::ConnectionState, so you can run a system in OnEnter(client:ConnectionState::disconnected) or otherwise read that state somewhere
ah, no, you have to call .get_state() on the Client to get the ConnectionState. maybe there's an event or trigger too
I think I will just make an UI system that gets state when so, basically get stuck in main menu
And you can try to reconnect now that i have no idea how to do that
is there any plans to bump lightyear to the 0.15-rc anytime soon?
I think I read that periwink is busy with life things at the moment. Maybe someone can prepare a 0.15 pr to help out
Yeah if noone is available I will do it, but I dont know the release candidate is just about finished perhaps just wait for 0.15
Hello, I copied this replicate_inputs system from avian_3d_character example:
pub(crate) fn replicate_inputs(
mut connection: ResMut<ConnectionManager>,
mut input_events: ResMut<Events<MessageEvent<InputMessage<CharacterAction>>>>,
) {
for mut event in input_events.drain() {
let client_id = *event.context();
// Rebroadcast the input to other clients.
connection
.send_message_to_target::<InputChannel, _>(
&mut event.message,
NetworkTarget::AllExceptSingle(client_id),
)
.unwrap()
}
}```
but it doesn't send messages for the `HostServer` client. For example, if I have 3 clients (1 host server + 2 more), the 2 non-hosts receive each other's inputs, but they do not receiver the host's inputs. Is there a simple way to emit the host's inputs as well?
Hm it should work, that might be a bug
In general I would copy the avian_physics example, I think it's more trustworthy
But this looks like it should definitely work. Could you please open an issue?
Thanks. I can investigate further/open an issue later today or tomorrow
@pine cape Mr Peri, Avian physics seen to be having a problem with inputs
I'm able to run a host server and 2 clients locally for the avian_3d_character example. I wrote the example so I may be able to help
thanks for checking. In my case, the players are moving correctly on each client, but what I'm having trouble with specifically is the ActionState on each predicted entity. The hostserver is using the replicate_inputs function to send clients' inputs to each other, but it doesn't work for the host player's inputs, if that makes sense
For context, what I'm trying to do is change each player model's animation based on that player's inputs. For example, walk animation if WASD are pressed, otherwise, idle animation. Originally I was just going to use things like velocity to determine the animation, but I don't want a walking animation if a player gets moved by something else for instance
Are you sending all except single?\
pub(crate) fn replicate_inputs(
mut connection: ResMut<ConnectionManager>,
mut input_events: ResMut<Events<MessageEvent<InputMessage<CharacterAction>>>>,
) {
for mut event in input_events.drain() {
let client_id = *event.context();
// Rebroadcast the input to other clients.
connection
.send_message_to_target::<InputChannel, _>(
&mut event.message,
NetworkTarget::AllExceptSingle(client_id),
)
.unwrap()
}
} yes you are
try just all
Holy schmokesy mind telling me what that visual interpolation plugin does, and why exactly you put sync config in postupdate?
check out the avian physics section here: https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html
oh
everything is clear now
app.add_systems(
FixedUpdate,
handle_hit_event
.run_if(on_event::<BulletHitEvent>())
.after(shared::process_collisions),
);
Might I ask why you do this, is an optimization technique?
do what, use the .after bit, or the run_if?
run_if
After is also interesting but i theorize is because you wanna despawn bullet first so two collisions dont do two damage
it's an optimisation to only run if there's an event to process, but also to self document so you know it's only needed for events. it was written before observers existed, perhaps observers would be more appropriate now
nowadays BulletHitEvent could be triggered instead of EventWriter'ed i think, then you could use an observer.
the /after is because process_collisions turns the avian physics collision data into BulletHitEvents, without the after you may end up processing the collision one frame later (eg if handle_hit_event ran before process_collisions)
My rule of thumb is like if happens on avg less than once in 3 ticks then it should be an observer (at least thats what my benchmarks tell me)
interesting, thanks. was wondering about that
tbh its just a random rule i made up for myself, microbenchmarks are never super reliable soo.. but from an intuitive standpoint smth like that makes sense too so yea
Wanna hear something interesting map_or for some reason was not as functional when it came to detecting if someone was hit as if let Ok and fill variable. Dont know why
did you ever fix this? the same log spam issue exists for the reverse situation: when a server disconnects and the client cannot receive any packets.
for now i've forked the project and emit a new event so my user-space code can detect a connection error
Fixed it now: https://github.com/cBournhonesque/lightyear/pull/698
How lightyear doing for coming 0.15 release?
I can do some of the bevy changes, but we'll have to wait for leafwing/avian to have 0.15 branches
I started a PR for the upgrade: https://github.com/cBournhonesque/lightyear/pull/700
there is one test failing for some reason
The leafwing test again maybe?

Yea seems to be the case
it never really worked soo shrug
No it's another one
I have a question on spawning a scene from glb and then perform the networking ncessary for sub objects, what are the ways I can achieve this?
How do i spawn on server and client and have them both sync up afterwards?
I think a scene is just an entity with some children entities, right?
I would either add Replicate to the parent of the scene, and all the children will get replicated recursively (if their components are registered)
or just add the scene on the client if some Message or Event has been received
Oh okay, i just looked at the logs and they had some references to lwim
Master Peri, if I have a physical action that needs to make my player look at mouse position. How would you code it my current go to is something like capturing mouse motion via leafwing and them setting angular velocity according to it. Is not going well
Yes
Thats exactly how
Hmm thing is my camera should also have a input map if she also is moved according to motion, I wonder if that is going to cause desyncs
Hm possible, just try ig
do the examples still work with steam?
doesn't seem to work for me, I'm trying out the lobby demo and after fixing the egui version mismatch i get this
2024-11-20T21:29:28.805964Z INFO lightyear::connection::steam::server: Steam socket has been closed.
CreateBoundSocket: ::bind couldn't find an open port between 27016 and 27016
thread 'main' panicked at lightyear\src\connection\server.rs:162:18:
could not create steam server: SteamInitError(FailedGeneric("InitGameServer failed"))
my existing steamworks code works (i was using renet before), so i know it should be able to work, i'm not sure what i'm missing to get these examples running with steam
Yeah that definitely makes sense. Hoping avian and leaf follows suit soon
Avian's main branch is on 0.15 RC already
has been since Nov 1st
but no actual release yet
no worries :)
Leaf seems to be too? Just not released yet either
Oh my bad that’s LW release
Not related to bevy version
@pine cape what do u think about having custom data in connect tokens? Kinda like jwts
Or maybe the correct approach would be to associate the data with the connect token and sign that somehow
hm
like the problem is that if you dont want to / cant communicate from ur game server to your backend but still want to have verifyable data from the backend u need to somehow associate that with the session & sign/encrypt it with a shared key
i use the client_id for that. since you pick the client_id when making the connect token, and the token is signed, i associated other data with client_id in another place, then lookup.. but yeah you need some shared comms/db
there is a small user data field isn't there? or am i remembering renet instead
yea but then u could immitate old state cuz its not directly associated with the current session, no?
256 bytes of USER_DATA i think
Idt so, but possible
/// Sets the user data that will be added to the token, this can be any data you want.
pub fn user_data(mut self, user_data: [u8; USER_DATA_BYTES]) -> Self {
self.user_data = user_data;
self
}
from the ConnectTokenBuilder
ah
you could pass small stuff like a nickname or something in that maybe
maybe instead pass a pubkey for the current session so we can sign that on the backend and can safely receive and verify any messages we get through the client from the backend
256 aint a lot
the connect tokens are already signed, so once a connection is established you can trust stuff the client sends you
maybe im overthinking it and should just communicate with the backend from the gameserver
tho i dont really like it
oh sorry you're talking about comms from client to backend which is separate to the gameserver..
yes
yeah i think you need your gameservers to be able to talk to your backend services somehow
i'm using NATS for that, since it's pubsub and also KV storage
Ah okay
if you don't need to expose it to the web, redis might be a decent choice too
but whatever you choose it's a bit annoying because you probably want to talk to it async from your bevy game server, so you have to do some plumbing..
Rj how do you override default vec in leafwing do you know it?
what do you mean?
Ugh..
Well alr
Like for example I want player to move in direction cam.forward, i need to override axis pair value
But it is weird is like as I override it resets
check this out: https://gist.github.com/RJ/e533e6f75cff3e5822a59c7972f0d5a3
that's for 2D, so the player turns towards where the mouse cursor is
Hey, anyone notice that the avian_physics example is a bit broken in main right now? Running in host-server mode right now. The client's box isn't moving in the client's game, but it is moving on the host server's client
the red box on the client, as far as I can tell, has PrePredicted, but not Predicted. There is also an entity on the client side with the single component Confirmed, pointing to the PrePredicted client box
If I spawn a 3rd client, both clients spam the following error message in the logs:
2024-11-21T16:22:47.572310Z ERROR lightyear::client::input::leafwing: received input message for unrecognized entity entity=Entity { index: 55, generation: 1 } diffs=[[], [], []] end_tick=Tick(32002)
And neither can see each other's box. The host-server can still receiver the clients' inputs and control the 3 player's boxes just fine though
I'll open an issue on github
i got P2P working fine, so it does work at least 🙂
Thanks, I found the issue. I recently distinguish between Replicated (an entity that is currently being replicated from the remote) and InitialReplicated (an entity that was initially spawned from a remote).
In the case of pre-prediction, the current ordering is:
- client spawns entity with PrePredicted
- server receives it and adds Replicated and InitialReplicated
- server observer notices the PrePredicted and transfer the authority from client to server, removing the
Replicatedcomponent (since the entity is now owned by the server) - the system in
server.rsqueries onReplicated, but the component does not exist anymore.
The solution is for now to update it to use InitialReplicated. It's a bit confusing annoying and not ideal but I don't see an easier fix right now
Some weird things are happening though.. even with the fix I can see desyncs, I wonder when these issues started happening
Like the client entity should be synced back to the Confirmed entity position in case of any desync
@river perch I should have fixed your immediate problem, but it seems like there might be an issue with how inputs are handled for pre-predicted entities. They seem to be received by the remote client but not by the client that has the pre-predicted entity. I don't know how I introduced this because I thought I had tested the changes, but I probably introduced a bug while updating the PrePrediction logic .. Created this issue: https://github.com/cBournhonesque/lightyear/issues/702
Awesome! I appreciate the quick fix. I am noticing those desync issues now as well... at least they don't seem to be manifesting in non-pre-predicted entities as far as I can tell
Curiosity, is it common to have such an amount of roolbacks? Right when spawning my entity
And it just keeps getting higher and higher
Found out what is was I was adding physics bundle every frame hihi
@pine cape Mr Peri is it possible for a component in a entity, have a different authority? Or only entities may have different authorities
Ah you can filter out what components to send via target
i encountered a small footgun switching from a system to an observer relating to PreSpawnedPlayerObject for bullets.
my approach to the blueprint pattern for spawning stuff that is replicated involves spawning with a minimal bundle which includes a marker component, then a decorator system that filters on Added<MarkerComponent> to insert all the non-replicated physics (etc) components. i tried to switch that system to an observer on Trigger<OnAdd, MarkerComponent>
i'm using PreSpawnedPlayerObject::default_with_salt(client_id.to_bits()) , and spawning can happen on client or server for bullets. when i switched to an observer, the server's compute prespawned hash code runs when there are just the bundle's components on the entity, but the client sees all the decorated components too. must be an ordering issue of some sort. it makes the hashes different, so i can't use an observer for the decorator unless i calculate the hash myself.
here's my bullet plugin showing the two options: https://gist.github.com/RJ/e08f16e4953267ed6cfbde39003af817
Observers generally run after all systems
It caused desyncs :?
I guess the correct approach is client replicate cam forward to other clients
And spin it in a shared system
I have it serverauthorative
Shrug
Makes sense if u want to do serverside culling
what's the best way on my client to detect when another client disconnects? I want to get the disconnecting player's Player component to find their name.
if i use bevy's RemovedComponents<Player> it's too late to grab the Player component. seems like i can listen for lightyear's EntityDespawnEvent but 99% of those are useless for this
i suppose i should just make the server broadcast a message for joins/leaves
Probably sending a msg from server
Yup
Especially since there may be other valid reasons that a Player entity is despawned, obviously depending on your type of game
yeah the components are probably added too soon if you use an observer. Should the hash only be computed using replicated components?
i don't think only using replicated components would make a difference in this case - my decorator system / observer adds replicated components, so there would still be a mismatch
in fact i think it only uses components with a netid anyway, so maybe it already does that
I see; in that case it's probably best to stick to using systems. The downside of observers is that they cannot be ordered
yeah. i'll probably use observers for that pattern for everything except stuff with PreSpawn
Maybe the hash computation could be done in a ComponentHook (which runs before any observers), I would have to think about the ordering to see if it could work
hm, yes maybe that would work.. that would run instantly on insert i think
not a big deal, just wanted to flag it in case i missed an easier solution
should work yes, component hooks would simply run right after command buffer application
This makes sense I am just wondering did you make sin cos implementation to your avian rotation?
that's built in to avian's rotation i think?
So I was making this function that makes my player look at camera, via action input
fn camera_rotate_to(
q_transform: Query<&Transform, With<MarkerMainCamera>>,
mut player_action_state: Query<&mut ActionState<PlayerAction>, With<Predicted>>,
) {
if let Ok(cam_transform) = q_transform.get_single() {
let (yaw, pitch, roll) = cam_transform.rotation.to_euler(EulerRot::YXZ);
println!("yaw {}", yaw);
if let Ok(mut action_state) = player_action_state.get_single_mut() {
action_state.set_axis_pair(&PlayerAction::RotateToCamera, Vec2::new(yaw, pitch));
}
}
}```
Weirdly my player rotate in a really weird manner no idea why
let camera_rotation = action_state.axis_pair(&PlayerAction::RotateToCamera);
let quat_rotation = Quat::from_euler(EulerRot::YXZ, camera_rotation.y, 0.0, 0.0);
character.rotation.0 = character.rotation.0.slerp(quat_rotation, 0.1)
What am I doing wrong
ah great i put my x on the wron place ?>(
In the avian_physics example I'm trying to remove Velocity in replacement for manually changing x/y
But manually changing the position doesn't handle collisions correctly with avian2d.
Any ideas?
if you want to manually move stuff around maybe use Kinematic not Dynamic bodies
doing compute_hash in a hook on PreSpawnPlayerObject fixed my ordering issues. I don't think there are any downsides: https://github.com/cBournhonesque/lightyear/pull/710
ah those recent dependabot updates for wtransport need some minor fixes too, will sort it
there are compilation failures because now lightyear pulls in hashbrown @ 0.15.2, and bevy 0.14.2 uses hashbrown 0.14.5. bevy reexports hashbrown, so i think the safest approach might be to remove lightyear's dep on hashbrown and just say use bevy::utils::hashbrown; whereever it's needed?
also made a PR to fix issues caused by recent dependency updates
You can also just calculate the physics like my previou x was and my current x is and add a velocity according to it
Might be a pain in the ass and avian does that for you with kinematics but sometimes you dont wanna change your rigidbody i dont know
Is there any way to replicate bevy events in lightyear? I suppose I could do it manually
Send message to target
Is a function that does that
You can send it from client to server client to another client and such
No direct way, but i agree that it would be nice to have
made an issue
Right now i'm trying to fix a bug where the avian_physics example doesn't work anymore for some reason.
The basic movement works correctly but it starts failing as soon as the player collides with a ball
how did the job hunt go? still hectic with real life things?
Got close a few times but didn't find anything i was willing to change for. I'm taking a break from it for a while
The bug seems to be that the input components ActionState and InputBuffer end up on the Confirmed entity instead of the PrePredicted entity for some reaosn, not sure why yet
Best way to send username and passwords from a system on client to a system/function on the server?
I guess as a message with some encryption on top of it?
Actually it's not that, i was looking at the wrong thing. The discrepancies only happen after a collision with the ball though
Disabling Correction completely fixes the issue, so it must be related to that. Also there are still rollbacks on collisions, which I thought had been fixed (cc @young prawn I think you had made collisions determinstic right?)
Oh yeah I was about to question that
Yeah. Thinking of making a message channel with this. But how do I actually generate a message and consume it. Found this
Spaceship, avian 3d, every single one flickers when collisions occura or rollbacks
Ah you can find an example of messages being used here: https://github.com/cBournhonesque/lightyear/blob/989bc9006eb19b702ff106591d52fb6882701fe5/examples/simple_box/src/server.rs#L157
Oh perfect just what I want
Should be deterministic, yeah. Recently I also added a proper cross-platform determinism test using a transform hash, it can be seen here
https://github.com/Jondolf/avian/pull/555
Oh well, good luck for the next round then :D
*with the caveat that I think it's currently only deterministic without the parallel feature
That's good to know, thanks
followed this example, just reversing it from client to server instead of server to client like in the example. but having issue that messages are not arriving at server sent from client, then i printed connectionmanager just to have a look at whats in there and I see all the messages just stays in "messages_to_send". any ideas why it could be stuck?
pub(crate) fn send_login_request(
_trigger: Trigger<LoginToServerRequest>,
commands: Commands,
mut client: ResMut<ConnectionManager>,
username_query: Query<&Text, With<UsernameInput>>,
password_query: Query<&Text, With<PasswordInput>>,
) {
info!("client: {:?}", client);
info!("Sending login request");
let username = username_query.get_single().unwrap();
let password = password_query.get_single().unwrap();
info!("username: {}", username.sections[0].value);
info!("password: {}", password.sections[0].value);
let mut message = LoginRequest{username: username.sections[0].value.to_string(), password: password.sections[0].value.to_string()};
info!("Send login message: {:?}", message);
client
.send_message::<LoginChannel, LoginRequest>(&mut message)
.unwrap_or_else(|e| {
error!("Failed to send message: {:?}", e);
});
info!("Sent login request");
}````
my code on client side looks like this.
and on server its like this:
```/// System to receive messages on the server
pub(crate) fn receive_login_request(mut reader: EventReader<MessageEvent<LoginRequest>>) {
for event in reader.read() {
info!("Received login request message: {:?}", event.message());
}
}```
protocol.rs has it all added like this:
```// Channels
#[derive(Channel)]
pub struct LoginChannel;
// Messages
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct LoginRequest{
pub username: String,
pub password: String,
}....
.... // messages
app.register_message::<LoginRequest>(ChannelDirection::ClientToServer);
//channels
app.add_channel::<LoginChannel>(ChannelSettings {
mode: ChannelMode::OrderedReliable(ReliableSettings::default()),
..Default::default()
});````
made sure systems added to the app
Are you running in host-server mode?
It's not obvious to me right now; are you sure that the client/server are connected?
Try setting the ChannelDirection to Bidirectional just to try
running using ```cargo run -- server````
I guess its similar to a client, server acting like one.
no success with changing to bidirectional.
ill go at it again tomorrow see if I get anywhere
Did you run client.connect()?
I just merged a simple change that should fix a subtle issue that was preventing PrePrediction from working correctly. The relevant examples (avian_physics, bullet_prespawn, etc.) should now be working much better!
The next thing i'd like to fix are the constant rollbacks in the avian_3d example, or the rollbacks that happen on ball-player contact in avian_physics example (which shouldn't happen if the simulation is deterministic and everything is working correctly)
Running commands.connect_server()on server and commands.connect_client() on client
Don’t think I’m using client.connect() anywhere in my code
Just to be sure I didn't miss anything. The best way to sync a 3d Transform is to create separate components for position, rotation and scale.
There is not a way to define for an existing component (like Transform) how it should be networked correct?
(like with quantization, interpolation etc.)
I am struggling a bit with how to organize my code again after a two month break, forgotten a lot, and trying to simplify and understand again.
I have this map with hexes (which are only local to the client there will be many more hexes), and insert Occupied and Base on them, as well as adding replicate. This works fine except for the mapping to the correct hex.
I store the hex on the Occupied and Base components and thought I could use that to look up the correct hex on the client. Attaching a picture, and some code that tries to explain it.
Ideally I would be able to lookup on receiving a message in my own maptable what entity (Hex) it belongs to.
Probably I'm going about the wrong way, but I hope intentions are somewhat clear. Any pointers?
base: Query<&Base>,
occupied: Query<&Occupied>
) {
for event in reader.read() {
if let Ok(occupied) = occupied.get(event.entity()) {
if let Some(parent) = map.entities.get(&occupied.hex){
if let Some(mut entity) = commands.get_entity(*parent) {
entity.insert(occupied.clone());
}
}
}
info!(
"Received component Occupied insert for entity: {:?}",
event.entity()
);
}
}```
broadly speaking, the client performs an action like clicking a hex to set it to occupied or whatever, the client input, eg ActionState, is sent to the server. perhaps the actionstate is an enum including ClickedToOccupy(hex_coords, etc). the server executes the logic to update the game state based on inputs, and any changes to hex entities on the server would autmatically be replicated to clients, as long as the components are in the protocol. So things like Occupied components would automatically replicate to clients. So you'd just do a query for Query<...., Added<Occupied>> or something on the client
if you're including entities in your messages/components you can implement MapEntities (iirc), check the examples for usage. that will translate the server entity to client entity for you once replicated.
A few months ago I had it like that, but I do not transmit each hex anymore. I have every command working on another branch, but the code to lookup stuff got too complex. So thats why the maps are now disconnected and not transmitted other than Base and Occupied
I looked at the MapEntities, but did not understand how I would be able to include my own mapping. But if it's the way to go, I'll try and explore som more.
the simplest approach is probably to have each hex be an entity with replicated components like Occupied, and use mapentities where needed. there may be an upper limit on scalability with that approach though..
not very lightyeary, but you could also have client/server store their own hexmap data structure and use command pattern: server broadcasts commands which mutate the map state, using an ordered reliable channel. then your map can be massive with minimal replication bandwidth, assuming commands are deterministic
This is kind of what I had before, and not very bevyish either. Trying to clean up and understand my code a bit better and "go with the bevy flow"
No, you can network Transform or GlobalTransform directly. An interpolation function for Transform is provided here: https://github.com/cBournhonesque/lightyear/blob/5845699d02ee953b73fa6f341224fe0df9dfa12b/lightyear/src/utils/bevy.rs#L10-L10
MapEntities is just required if you have components that contain other Entities.
Lightyear will make sure to 'translate' the entity inside the component from the local world to the remote world when it's replicated. Apart from that you don't really need to worry about much.
Does your Occupied or Base component contain another entity?
No they don't. They just don't get attach to the hexes on the client. since they don't match up between the worlds.
And I'm not sure it's solvable either 🙂 Just something I'm trying
No but I mean what does the component actually look like?
Is it
tile: Entity
}
or
pub struct Base {
tile_x: int,
tile_y: int,
}
and what is Hex
is just ```pub struct Hex {
/// x axial coordinate (sometimes called q or i)
pub x: i32,
/// y axial coordinate (sometimes called r or j)
pub y: i32,
}
Then why does it not work? You can just use the Hex x, y to find which entity is being referred to, no?
Because when I want to show the base indicator (the red in the image), that is a transormation on child of the Hex, with an Occupied component. I run HostServer, and the image is on the server. On the client the Base and Occuped does not attach to the Hex (understandebly so)
But you are right, I am thinking about this in the wrong way again I think. I'll take a step back, make some food and start again.
Sure, let me know. But this situation can definitely be handled with lightyear. Parent-child hierarchies can also be replicated
Cool. I'll check out that before I start going further.
Is it wise to rreplciate transform tho? As of my knowledge a lot of bevy components contains pre bundled transforms
Ah this would be very cash moneh
FYI; I reverted back to to keeping the in game state (related to each hex) beeing totally seperated from the visual hexgrid. If the entity ids were predictable (for the visual hexgrid, looking at Entity:from_raw) I think the approach would have worked out of the box. But since it was warned not to use it, I did not go that route. Also I could not figure out how to bend lightyear my way with regards to this, but it works pretty good as it is now, like @wintry dome described.
Where can I see what port by bevy server/game is running on? Would it be possible to run multiple bevy worlds on different ports on the same server?
You choose which port to use in the server config; and yes you should be able to have multiple bevy words on different ports
what's a non-bevy way of accessing number of connected clients to existing lightyear server? want to try to do sequential clientid assignment and let user just do cargo run -- client and get the next available spot.
the clientid is inside the connect token. so in a production setting whatever issues connect tokens could keep track if you want sequential numbers. for dev, using Authentication::Manual, I use a random number unless it's specified with a command line arg.
any benefits to using random number in production or is it just easy that way? with sequential I guess there is a larger risk of connect token/client id collisions.
i'm generating random clientids for prod, but i don't have it linked to a database or anything. i suppose if you have a db of user accounts you might make the clientid the same as the primary key for the user table or something like that
random is easiest unless you already have a user database i think
if you really need to know the join order of players i would insert a specific component with a sequence number for that, rather than depend on client id
yeah thats the thing, im working on a user database rn. I dont see how client_id is related to say a userid, so i was thinking of keeping userid and clientid seperate and let only lightyear take care of client ids, and userid is database stuff. since client_id kinda is just the id to the actual device/machine thats connected? or am i misunderstanding the meaning of a clientid?
only clientid relation ill make in the user database might be a column like "last_clientid" and "current_clientid" assigned upon login and logout. incase I gotta debug and ive logged out clientid instead of userid somewhere.
i think as long as you have a way for game servers to lookup the user from a client id it'll be fine
found where this is done. but think ill just do random client_ids for now too. will keep them short for easier reading until i figure out seq numbers. not really a big deal having them random anyways. the chance of it being assigned a identical one as a already connected client is probably pretty slim at u64.
@pine cape So I am refactoring a little bit of the avian_3d example, and I noticed there is a setting called max_prediction_ticks, I think now is called maximum_predicted_ticks. Also it seens that feature might be buggy, a least a few discussions mentioned it is it still? Although I am not sure is this comment still valid https://docs.rs/lightyear/latest/src/lightyear/client/prediction/plugin.rs.html#80
Source of the Rust file src/client/prediction/plugin.rs.
Yep it's buggy still
I haven't figured out how to update the InputBuffer if the input delay is changing dynamic ally
That's the only missing piece
Kays, also in avian_3d_physics, the parallel feature is enabled might be breaking deterministic
Gonna fix it and check it out
I think even without parallel there is no determinism, or there's still extra rollbacks for some reason
Yup you be right on that
@pine cape Stale inputs ...
?
I think i figured out what it is, I will test it out and make sure
Finally got around to working on the webrtc transport thingy :)
Its a lot more annoying to implement than ws/wt but whatever. One question tho, is there any way you know of to use env or smth with rust cuz having to hardcode turn server credentials into the app sounds kinda like a bad idea
@pine cape
maybe i should just let the user handle that and just let them supply an basic/bearer auth string
its a bit awkward security wise either way since any user could just "steal" the turn server credentials no matter how we store it since they need to authenticate with them
Maybe the best option would be to generate credentials for each new connection on the fly
but again thats something the user has to do themselves
i have my server generating a new self signed cert each boot, which is fine when there's a matchmaeker that can pass the digest to the clients. would be useful to have an http server plugin that lightyear could use to serve up the connect token and cert digest. would make wasm clients much easier to play with
http servers are pretty heavy tho, this should be a separate app / server
Maybe useful for dev but thats all i think
I finally got time to setup my nats server up and running last night. Felt great. Will continue on the bevygap stuff later tonight
++
One of the examples shows how the server can pass the connect token to the clients
I was thinking of wasm clients. I think the example uses a TCP socket
@pine cape I think the issue was correlated to input delay, i increased by 12 and now he seens to have low amount of rollbacks. Even with more clients
For some reason when he reached 3 KABOOM
Now he doesnt do that anymore
It seens the delay caused for very stale inputs to be passed to the apply_character action function causing some sort of rollback loop
Well if delay is high there is no delay yea
But even if 0 delay, there shouldn't be any rollbacks with only one player
But there are
In my current state I only have one
But only if I eliminate the block from sample
pub(crate) fn set(&mut self, tick: Tick, value: &ActionState<T>) {
let Some(start_tick) = self.start_tick else {
// initialize the buffer
self.start_tick = Some(tick);
self.buffer.push_back(BufferItem::Data(value.clone()));
return;
};
// cannot set lower values than start_tick
if tick < start_tick {
warn!("You set a tick lower thant the start tick in input buffer");
return;
}
The cause lied in this function https://github.com/cBournhonesque/lightyear/blob/5845699d02ee953b73fa6f341224fe0df9dfa12b/lightyear/src/inputs/leafwing/input_buffer.rs#L99, when he has a input delay of about 6, he tries to grab a tick that is a lower than start tick so he just returns and doesnt set tick.I added a warning but basically the entire vecdeque from buffer breaks
He goes kaboom like this
Now why dont know probably because input delay diminishes by -6 or something like that, going way before start tick
Can you try adding an explanation in the PR so I can review?
It is not fully functional I am just trying to figure out what is causing the rollbacks. But alas you can see what I am trying to do
Nice. Do you mind sharing how you fix them? I might be encouraging similar issues with my game
Hey, I finally got around to trying this, but it's not triggering automatically, and I don't understand how it would do it automatically, but it seems you have to do something extra. I put printlns in the implementation and it's not being called.
I have this for now:
#[derive(Component, Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Inputs {
Direction(Direction),
Spawn,
Target(Vec<Entity>, Vec2),
None,
}
impl MapEntities for Inputs {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
match self {
Inputs::Target(entities, _) => {
for entity in entities {
*entity = entity_mapper.map_entity(*entity)
}
}
Inputs::Direction(_) | Inputs::Spawn | Inputs::None => todo!(),
}
}
}
How is this intended to work?
You also have to add something like .add_map_entities() when registering the component in the protocol
Okay I figured there was something like that, I just couldn't find it when searching, thank you!
Hmm, I've run into a bit of a sang. The thing I'm trying to entity map is the inputs, as you can see, however internally when you add the input plugin it adds a message of InputMessage<Inputs>, but there's no implementation of MapEntities for lightyear::inputs::native::InputMessage. It looks like there's one for the leafwing InputMessage though, so I'm guessing this was just forgotten?
Also the user experience of mapping entities for inputs can definitely be improved, as it's not immediately obvious that you need to wrap your InputMessage.
Also I saw your comment in add_map_entities: "or should we just have add_message_map_entities and add_component_map_entities?", and to answer your question, if you split it up you can add extra bounds on the two new functions, Message and Component respectively, which would remove the issue that I just had of calling add_map_entities::<Inputs>(), which silently does nothing
For now I'll try migrating to leafwing to unblock this, but I'm happy to raise a ticket (and make a PR if want 😄) for this
I didn't expect you to apply MapEntities on the inputs, I don't think this is supported. Even the MapEntities implementation for leafwing inputs is for pre-prediction and not for users
Ah, hmm
Maybe I'm using inputs for the wrong purpose. I'm trying to have an input that when you right click it'll update the target position for the currently selected minions (so it takes a list of minions and a new target position), and then in the server input handler it updates the targets, which then updates the minions (and it's then replicated and stuff), should I just be using a normal message for this?
I would use normal messages for this
but I think it makes sense that inputs should try to support stuff like that
i'll open an issue
Also the update for bevy 0.15 is basically ready, I'm just waiting for new avian and leafwing versions to be released to also release a new version
Now that I look at it closer that'll make the implementation for the leafwing InputMessage really difficult, since you can't have a different implementation based on whether the inner type supports it or not 🤔
yes I would have to change the way leafwing input works
@pine cape Question replicated entities inbound from server that are predicted should they cause rollback with one sole client?
No there should never be rollbacks with one client
If client and server worlds are deterministic
That's why avian_physics shouldn't rollback on collisions
Perhaps sleeping is screwing things up too once I disable it they stay at 0 unto a collision occurs
Btw rollback-ing resource should be supported since https://github.com/cBournhonesque/lightyear/pull/622
WEll hot digiity damm
How I rollback it tho i know how to replicate it
stepper.client_app.add_resource_rollback::<TestResource>();
Lovely thanks
Oh, another todo is to switch the interpolation logic to bevy's new Curve trait
@pine cape Yeah warm collisions was the cause of breakaage
Does anyone have good ideas for how to implement prediction ergonomically? The only way I can come up with is to implement it twice, but one adds an extra filter for Predicted, and then two systems. Ideally I would have a system that I could just add to both my server and client plugins and it would just work
My prefered method is spawn in server replicate with sync target, in client use added predicted. It keeps things nice and umderstandble
Also necessary when using lobbies
I do that too, I meant for like predicting the movement of an NPC (or the player) I have to implement the logic twice, since the client needs it to only affect Predicted creatures, but on the server it should affect everything
Obviously in a simple case the only change is adding a filter, but in the most complicated case you have to change the logic slightly to account for the fsct that you may need data from all of them, but only update some
You can have a shared system with a Filter that works for both server and client-predicted entities: https://github.com/cBournhonesque/lightyear/blob/main/examples/bullet_prespawn/src/shared.rs#L186
I use ReplicationTarget on the server to only target entities that are replicated to the client
just let me know if your PR fully fixes the rollback issue. What do you mean by warm collisions
Something in avian, some sort of magic that stored previous collision values to apply contact forces
After removing it the rollback loop
Stopped
You disabled it via
app.insert_resource(NarrowPhaseConfig {
match_contacts: false,
..default()
});
```?
Yeah
Interesting enough we still have rollbacks only when collision occurs with two clients, but I oughta to think that is expected
Also they dont seen to be uncontrollable, the remain decent so just like once in a while
Sincerely I disabled so much stuff that I dont even know anymore what is fixing it
yes that is unavoidable, the only thing to fix is rollbacks happening with 1 client
I tried running your branch but i still see constant rollbacks on collisions, with only 1 client
wut
Might I see it? On my end I only have some weird rollbacks when spawning
i'm just jumping on the purple cube
Interesting, on my end
Does it occur frequently? Oh wait it sometimes occur sometimes it doesnt
Well at least I can say it did help
I'm tweaking my leave/enter lobby and trying to stop and start the server and connect, and I see some leaks (I think) with regards ControlledEntities which seems to remain after stopping the server in HostServer mode.
on_connect_host_server spawns it, but on_disconnect_host_server does not despawn it.
Not sure I'm looking at the correct spot in the code though, could there be a bug?
I can remove it manually, if my scenario is a bit off
Yeah I tried everything, removed each component that might be causing rollbacks on collision but well nothing. Interesting enough sometimes it works, sometimes it doesnt. Probably is deterministic and sometimes it isnt. When more strong collisions or multiple occur it seens determinism goes kablooei
When simple ones occur, like static rigid for example it remains okay
Perhaps advancing with deterninism test would be more efficient
Good catch; this should fix it: https://github.com/cBournhonesque/lightyear/pull/722
Thanks for the quick turnaround!
when registering a component, how do I make it such that it just syncs by overriding rather than having a interpolation?
interpolation:
app.register_component::<MaxHealth>(ChannelDirection::ServerToClient)
.add_prediction(client::ComponentSyncMode::Full)
.add_interpolation(client::ComponentSyncMode::Full);
is it like this?
app.register_component::<MaxHealth>(ChannelDirection::ServerToClient)
.add_correction_fn(|_, x, _| *x);
sounds like you want add_prediction with ComponentSyncMode of Simple
that will just replace the client's component on the predicted entity with server values as they arrive without any interp
I see
You can also just completely disable prediction/interpolation and just let the replication override the value every time it's updated
so don't call add_prediction or add_i nterpolation - does that mean the client only has a confirmed entity?
I'm curious, if I decided to send 100000 messages along a reliable ordered channel, is it guaranteed that all 100000 will make it to the other side? Or is it undefined what happens when you do that kind of extreme thing?
Replicated in this case, confirmed only occurs with predicted entities
good to know!
wait if the entity is interpolated and predicted at the same time, how wud it work?
cuz I have other components that are both predicted and interpolated in the same entity
has anyone tried hosting server via port forwarding? how was the process?
Although your entity has a replicate struct that interpolates and predicts, doesnt mean your component should necessarily follow the same replication logic
Since it is max health, i think the goto should be
app.register_component::<MaxHealth>(ChannelDirection::ServerToClient)
.add_prediction(client::ComponentSyncMode::Simple);
Simple only mutating when you tell server to mutate that component
I see so in this case, only server shud be mutating and client shud not touch it right (if we want it to be in sync that is)
Exactly
You can make it so client defines it but you know if competitive great chance hackers might abuse that
ok I tried, it is as simple as portforwarding then sharing yr public IP
You should get the 100k messages
3 entities are created: the Confirmed (is updated every time the server gets an updates), the Predicted, and the Interpolated
Just like how u would do that usually, open the port on the firewall and then forward it trough your router
@pine cape , I would just like to give a shoutout. Once I got past not understanding anything, to understanding something, your library works GREAT! Really awesome work has been done in lightyear!
Thanks! Really appreciated
@pine cape Mr Peri how can we buy you a cofee?
I followed the style of built you used on steam
And it was perfect
Also you should clear your stale branches
No need, just appreciating the library is enough 😉
@pine cape Gonna be honest here, perhaps it would be best we make lifetime persistent the common usage the amount of warning I get because of entity despawn before hand errors is a biggie, just my opinion tho
the easiest solution might be to have a despawn_without_warning function in bevy
Do you get this on the client?
The warnings should be fixed by https://github.com/cBournhonesque/lightyear/pull/724
Upon disconnecting the client would often be spammed with annoying log messages such as
2024-12-06T19:22:08.662684Z WARN bevy_ecs::world: error[B0003]: Could not despawn entity Entity { index: 22,...
Fire, now when I test my bullet spawn, it doesnt annoy me. Still gonna use persistent, that way I HAVE THE POWAH
Oh btw I am making a tutorial about your crate if you dont mind
Can you dm me or tag me the link, if this becomes available at any point? I'm new to bevy and lightyear and I don't really understand how messages are supposed to be transferred across applications (server/client). I've looked at the examples, but I can't really find out how to do it:
I looked at some of (https://github.com/search?q=repo%3AcBournhonesque%2Flightyear MessageEvent&type=code).
Here's my pretty minimal example:
I define a Message: https://github.com/phaack/troopy_bevy/blob/b2872634c470a7f7315e145a28eb5bec62100b1d/src/game/networking/messages.rs#L15
Then I send that even on the client:
https://github.com/phaack/troopy_bevy/blob/b2872634c470a7f7315e145a28eb5bec62100b1d/src/game/core/interaction/ui.rs#L151
Then (onUpdate?) I try to receive the Message on the server:
https://github.com/phaack/troopy_bevy/blob/b2872634c470a7f7315e145a28eb5bec62100b1d/src/game/networking/server.rs#L72
Where the client and server use the protocol w/ SharedPlugin:
https://github.com/phaack/troopy_bevy/blob/b2872634c470a7f7315e145a28eb5bec62100b1d/src/game/networking/protocol.rs#L50
You are doing it wrong here https://github.com/phaack/troopy_bevy/blob/b2872634c470a7f7315e145a28eb5bec62100b1d/src/game/core/interaction/ui.rs#L151
You are not defining the target, and you are actually using bevys, send function
Instead of lightyear send_message, this is how you send a message in lightyear
fn my_system(
tick_manager: Res<TickManager>,
mut connection: ResMut<ClientConnectionManager>
) {
// send a message to the server
connection.send_message::<MyChannel, MyMessage>("Hello, server!");
// send a message to some other client with ClientId 2
connection.send_message_to_target::<MyChannel, MyMessage>("Hello, server!", NetworkTarget::Single(2));
}```
@split violet https://www.youtube.com/watch?v=sJL4ClWfioE&ab_channel=altpotato Sure I am about to actually targer that theme
Version 0.14.2, will be migrated in the future dont worry.
Repos mentioned:
https://github.com/Sirmadeira/psycho_project
https://github.com/dimforge/bevy_rapier
https://github.com/cBournhonesque/lightyear
Dont forget to give the mantainers some love
Alright, but how do I receive the events then?
fn received_send_troops_message(mut events: EventReader<MessageEvent<SendTroopsMessage>>) {
for event in events.read() {
println!("Received SendTroopsMessage: {:?}", event.message);
}
}
Doesn't seem right as a system. (At least I don't seem to receive anything) when using:
let mut msg = SendTroopsMessage {
action: SendTroopsAction::default(&start_entity, &end_entity),
player: Player::new(Some(1)),
};
let _ = connection.send_message::<GameChannel, SendTroopsMessage>(&mut msg);
(https://github.com/phaack/troopy_bevy/blob/3de52e043d44f3eff927111eb00b37eaec2b9292/src/game/networking/server.rs#L82)
Sorry, if this is somewhere in the docs, but I seriously don't see much in the docs. I guess I must be blind
I also make sure to add the channel, so that shouldn't be a problem:
app.add_channel::<GameChannel>(ChannelSettings {
mode: ChannelMode::OrderedReliable(ReliableSettings::default()),
..default()
});
``` (https://github.com/phaack/troopy_bevy/blob/3de52e043d44f3eff927111eb00b37eaec2b9292/src/game/networking/shared.rs#L34C9-L37C12)
I'll check out your linked video out now, sorry if that's already shown there.
https://github.com/phaack/troopy_bevy.git I dont see anywhere in your code where you do this send_message
I think you might need to refresh the page, if you still have it open from previously - I pushed a fix like you said from above: https://github.com/phaack/troopy_bevy/blob/3de52e043d44f3eff927111eb00b37eaec2b9292/src/game/core/interaction/ui.rs#L155
[I'm also certain that it actually gets executed]
has anyone used lightyear wasm client in firefox sucessfully, with webtransport? it never seems to work for me. (fine in chrome)
yea but you need to use ff nightly iirc
cert hashes arent in stable release yet
or smth like that
ah, ok so hopefully not too long before it Just Works?
i think its a bit like with safari support, not enough ppl care about WT because it only has the quite niche usecase of gamedev atm
Setting that replication group for characters in avian_3d_character to something not used by the purple blocks appears to stop the constant rollbacks.
Now, rollbacks only trigger when a character interacts with a purple box but will constantly trigger rollbacks until the boxes stop moving despite the character no longer interacting with it before it stopped. Not sure why. There are rare occasions where the rollbacks do not stop ever. More characters results in higher chance of nonstop rollbacks
Spawning the boxes with an initial velocity (thus motion without the intervention of a client) does not trigger rollbacks.
Characters interacting with other characters does not trigger rollbacks and yet characters interacting with the purple boxes does.
Hm I would expect that all predicted entities have to be in the same replication group to guarantee that rollbacks work correctly
i'd like to be able to compile the examples as dedicated servers without having to compile steam or gui stuff. although you can run them just as a --server at the mo, it's all still compiled in
i'll add suitable features to examples_common
Hmm what fixed constant rollback for me was increasing input delay which makes sense when you come to think about it
Constant rollbacks on dynamic bodies collision tho seens to be an issue correlated to avian determinism
I think Periwink had a test for that, interesting enough with rapier I dont get the same result as in there is no rollbacks with collisions
Is there an observer for connect events?
It will be pretty cool if it did
I added some log statements before and after the physics systems execute. On the server side, a purple block will be completely stationary (it's position, rotation, & velocity are unchanging). On the client side after a rollback where a block's physics-related components sync with the server, I see that after the physic systems execute there is an incredibly small linear velocity applied to the block that de-syncs the client block's position and rotation by an incredibly small value. Before the physics systems executed, the block was sync'd and did not have any velocity, force, impulse, or angular velocity. There shouldn't be any velocity applied to the block as it should be completely stationary. My guess is that there is state information within avian that avian_3d_character isn't rollbacking.
I also tried disabling contact matching in the narrow phase:
app.insert_resource(NarrowPhaseConfig {
match_contacts: false,
..default()
});
I also tried setting avian to the commit https://github.com/Jondolf/avian/pull/480 (currently lightyear uses an older version of avian that doesn't include that commit) but to no avail
Is client world, a new world type manipulated by you or is it just bevy native world?
just bevys native world
ConnectEvent on client side: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/client/networking.rs#L373
ConnectEvent on server side: https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/server/events.rs#L57
I thought that lightyear was using a version that had https://github.com/Jondolf/avian/pull/480
It's possible that some resources that are needed are not being rolled back
It might be possible to just rollback these resources
You mean collisions?
Ah you're right, my bad. I didn't do a git pull in a while and see that the dependencies were updated.
There's a few resources that are used by avian to handle collisions correctly, yea
just a thought:
- have two servers
- one gets the authority for handling all the physics and other gamelogic related systems
- the other one is the one that all clients get connected to and handles all io and replicates the world forward to the clients from the "game logic" server while preprocessing inputs for the game logic server
isnt that basically star citizens server meshing with native lightyear? :o
not sure if this actually works but if it does would be a cool poc
s1 would need good single core spd and s2 would need as many cores as possible to handle as many clients as possible
smth like that
if anyone can't build on wasm: (will fix properly in due course, down a rabbit hole atm..)
# TODO 0.3.3+ is a breaking change.. pin to 0.3.2 for now
bevy_web_keepalive = "0.3.2"
field name in WebKeepalivePlugin changed. and bevy 14->15
oh no i totally forgot doing that 
shoulda been a major version change
err minor
But not patch
good to know, thanks!
it's always a painful time waiting for various deps to support the newest bevy versions after release
yea
whats wrong with having a branch that doesnt compile for a few weeks :'D
i'm adding some conditional compilation bits to the examples and refactoring cargo files in order to be able to build the examples as headless servers and/or wasm clients. will do a draft pr for more discussion once i have something legible to show
ultimate goal to conditionally compile in bevygap plugins and host all the examples on edgegap 🙂
that would be so cool
would be great to provide an easy route to hosting a game on edgegap if you use lightyear. making a simple game work locally is only half the battle at the mo
If a user acess via the same local machine, is there a chance it is client_id would differ?
For example, in save_files I intend to use client_id as acessor to that user info. Or would I need to make some sort of unique user and password for it? If sowell fuck
You can choose the client_id
What does that entice?
Imagine a on production environment, tho. Would I be able to choose it is client_id even so?
in production your game will contact a matchmaking service (a different app to your game server), which will give it a ConnectToken (and game server ip+port). this matchmaking service can decide what client_id to put into the connect token. sometimes this requires the user to sign in via oauth or something, so the client_id might be related to a database record for the user.
Ah I see
Bidirectional component will only mutate bidirectionally, if client is also replicating it?
can anyone help with my cargo/deps problem?
bevygap has a dependency on lightyear.
i'm currently working on a feature branch of lightyear, and i've added bevygap as a dep to lightyear.
I want bevygap's lightyear dep to use my local checkout of lightyear (ie, path = "./lightyear") but i don't know if this is possible with cargo's patch system. can't get it to work.
if i don't use the exact same version of lightyear then it fails because bevygap code can't find the lightyear resource (because bevygap has compiled a different version of lightyear so it's technically not the same resource the game has inserted)
Hm interesting. Why wouldnt this work tho?
[patch.crates-io]
lightyear = { path = "./lightyear" }
# bevygap for deploying on Edgegap
bevygap_server_plugin = {git = "https://github.com/RJ/bevygap.git", tag = "v0.2.2"}
bevygap_client_plugin = {git = "https://github.com/RJ/bevygap.git", tag = "v0.2.2"}
# Make sure bevygap's lightyear dep is our exact version:
[patch."https://github.com/RJ/bevygap.git"]
lightyear = {path = "./lightyear" }
i tried this, since my bevygap dep is from github
Okay maybe to clarify;
your structure is like this?:
a = lightyear depending on bevygap
b = bevygap depending on c
c = current lightyear without bevygap
now you want b to use a instead of c?
in fact, in the bevygap cargo file, it has this dep for ly:
lightyear = {git = "https://github.com/cBournhonesque/lightyear.git", rev = "5845699d02ee953b73fa6f341224fe0df9dfa12b", default-features = false}
structure at present without patches is:
lightyear (local checkout) depends on bevygap from github
bevygap from github depends on lightyear pointing to a specific github revision
i want to patch it so bevygap uses the local checkout of lightyear
okay that should work, the other way around would be circular dependency which im pretty sure doesnt work
using cargo tree i can see that the bevygap_client_plugin (for example) is causing the wrong lightyear dep to be pulled in. ie it's pulling the one from its cargo file, not my patch to point to ./lightyear
Pfew yea no idea why, as far as i understand thats the way you are supposed to do that
https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html
maybe its trying to pull the dep from local cache for some reason? Unlikely but i'd at least check and clear build cache
oh wait, i've committed my cargo.lock to bevygap repo, that seems like a bad idea
removing that lock file didn't help
and i nuked my local lock file and did cargo clean 🤔
:/ im out of ideas
ah i think i've got it:
[patch."https://github.com/cBournhonesque/lightyear.git"]
lightyear = {path = "./lightyear", version = "0.17.0"}
that patches anything trying to use that github url for lightyear, which is what bevygap does. phew.
and pretty sure the version not necessary
gg 💯
I wonder how hard it would be to support https://github.com/projectharmonia/bevy_enhanced_input in lightyear. I'm guessing adding another input crate introduces a ton of edge cases, and if it lives in the main crate it introduces yet another bottleneck for keeping lightyear up to date with bevy. So probably, in order to support it you'd need to abstract away LWIM support in Lightyear behind some sort of input adapter interface
(and then implement support for LWIM/bevy_enhanced_input as separate crates implementing the interface)
in chrome^
i've got the build system for wasm and servers working, but i've only deployed spaceships and simple_box so far: https://rj.github.io/lightyear
hi whos on the german server
seeing a little bit of stuttering/flickering
hmm your ping is ~80ms. can't remember what the input delay and settings are tbh. it's not at all tuned well
pretty smooth here, at ~67ms
it is smooth like 95% of the time
i'm working on the infrastructure for deployment atm, but def want to get that buttery smooth at some point
some of the flickering was purely graphical. like, the border around the arena was flickering a bit during rollbacks I think
tbf i'm in an environment with uh... adversarial network conditions aka terrible wifi, which might explain the stuttering
The cause of that is rollback on physical collisions
A VERY annoying bug
some non-determinism still lurking yeah
The stuttering is probably non adaptable input delay, with high latency certain configs should be adjusted
i don't think the input delay is auto-adjusting based on lag, perhaps it should be. and need to review defaults / minimum value. i feel like it shouldn't drop any lower than input delay of 3 ticks for spaceships
i have a PR onto my own fork so i could trigger workflow runs. here's the changelog and todo list: https://github.com/RJ/lightyear/pull/1
Awesome! simple_box works well for me, but on spaceships my inputs don't seem to do anything
I have added settings to adjust the input_delay based on lags; but there are still some edgecases where it doesn't work. (for example if the input_delay increases on a given tick, then you might have two inputs sent for a given tick, or no input sent for a tick)
I think it should be do-able; I would maybe first try to add support for bevy_enhanced_input directly, and then abstract it away once the similarities are better understood
I noticed this, fixed with a refresh weirdly
LWIM had some careful work done to make sure that inputs work well within the FixedUpdate schedule, we would have to check if bevy_enhanced_input has the same
Yeah the re-render on rollback is a bit annoying, idk why that happens
dumb question, by default there aren't any conditioners added that would cause visual jitter, right?
No. Best guesses are that you are either experiencing rollbacks or you’re using the fixed schedule but not interpolating transforms
i am interpolating the transforms, but badly! 😅
i really want to use avian physics but the lack of motor support is a blocker, so i'm using rapier. syncing up the transforms has been tricky fs
if rapier uses transform/velocity as the source of truth, what's the best way to interpolate those values for clients? would it be adding transform/velocity to the protocol or creating a new component that copies the source transform every fixed update?
i'm confused as to whether the replication is reading the world state during the fixed update or the normal update
Replication is reading the world state at a given tick t during FixedUpdate
Yes I would try adding Transform to the protocol
@pine cape the issue where spaceships occasionally ignores user input.. i'm suspicious that it's because here: https://github.com/RJ/lightyear/blob/examples/examples/spaceships/src/client.rs#L81 maybe Controlled is added too late, and the entity already got replicated to the client without Controlled, and Controlled would probably come in the next packet. does that sound plausible?
it looks like that shouldn't be possible. the spaceships server inserts the Player component and the ControlledBy all at once when spawning ships for newly connected players.
could it happen that clientside the Predicted component is inserted on a different tick to Controlled (or my Player component i suppose), causing that system not to notice our own player? i wanted to look at the "copy replicated components from confirmed to predicted" logic when an entity is received on the client, but can't find it
To be completely honest the added based systems tend to be a little unstable. Because of the when we replicate our other component. Of course you could ensure running them after the set responsible for replication. But I personally just make observers
With rapier I think it is simpler, since he directly applies changes in transform. You can just make transform itself the rollbackable. Of course you will need to ensure system ordering is okay and that he ticks according to fixed_timestep_hz.
I think you will also need to make him visual interpolate, via the visual interpolate plugin. No 100% sure on that one.
hm, well i caught it with the ignoring-inputs error but it definitely detected the controlled player and added the input map, so back to the drawing board.
it would probably be nicer as an observer now, but i have to find out what the cause is first..
It's in the client/prediction folder, it's weird that this doesn't happen with fake network conditions
@pine cape do yk if i just need to impl this myself for js val?
or should i convert this into a str?
cuz this is a js Error wrapped in jsvalue
so in theory i can just .toString() it

this seems to work
@pine cape Say Mr Peri, I have a currency struct. And of course I want my server to validate if player can have that amount of currency. My currenct workflow is as follows, I change predicted entity (Player in this case) and inform server of that adjustment. Server checks and if he okays it he changes confirmed, and the resource that stores the currency amount of that client. If not he rollbacks, is that the correct approach?
You just have apply the same logic on the predicted or confirmed entity. (client vs server)
It's possible that the predicted entity has a different currency amount temporarily, but eventually the server value will become the source of truth
Same with collisions; it's possible that on the client there is no collision. But on the server that actually is a collision, so the entity loses some health points on the server and the changes are applied via rollback to the client entity
Wait so the logic confirmed ->predicted
Oh my
well not gonna lie that lowkey kinda sucks when you want to apply local changes, and later validate them. For example, players tests skin. Can he still hold it after?
But i guess it makes sense
Perhhaps I shoudl try to update lobbies example to use rooms, in my view they are pretty much the same thing
It already uses rooms
i wrote a cli tool to interact with the edgegap api, so now examples/ server builds can automatically update the docker tag configured in edgegap for insta-deploy https://crates.io/crates/arbctl – now i can say arbctl application patch-version simple_box v1 --patch /docker_tag '"sha-1234567890"'
i'm using it to migrate all the examples stuff into a Lightyear org within my edgegap account for now
oh yeah it does well i be dammed
I tried upgrading to the latest leafwing version, seems to work well.
Although the examples have a bunch of changes needed + bevy-screen-diagnostics
cool. are all the other lightyear deps on 0.15 now?
just need avian, which will upgrade end of this week
bevy_screen_diagnostics might need some more work though... maybe i'll switch to one of the other libraries
there's iyves something or other i wanted to try
yea same
Customizable Performance/Debug Overlay for Bevy UI - IyesGames/iyes_perf_ui
Customizable Performance/Debug Overlay for Bevy UI - IyesGames/iyes_perf_ui
lol
jinx
not released 0.15, but seems to be on 0.15-rc3
quite a few changes to examples stuff in my examples branch, gonna suck to merge that after 0.15 change.. but i've only done 2.5 of the examples so far
actually should be fine to upstream what i've done, i left the default features so they would behave as it does now (building gui for everything)
could be worthwhile, because i moved some of the common ui stuff to lightyear_examples_common, so that would probably be the place to add a debug perf ui. but i don't have much free time for the next 3 weeks, and it might be a pain if you're doing 0.15 work.
did you take a look at the diff?
are you happy with the cargo file refactoring to push deps up to the top level workspace cargo file? i think it's sane, not sure if there any downsides apart from a big ugly diff.
Perhaps it would be wiser to just make a very simple perfui on lightyear itself
For what I know all the variables like rollback and such are easily acessible
yeah i'd prefer that actually
I love the changes, I think they make sense!
We can do it in whichever order you prefer, i'm fine with merging your changes before the 0.15 upgrade
Your changes are amazing btw
great. i responded to the review comments you left. i'll try and get things tidied up enough to merge, even though i've only got a couple of the examples working on wasm so far. we'll just need to disable the new workflows while i'm still working on it in my fork.
looking at using lightyear for a project, and I was curious if there were any plans or context around server migrations (or w/e it’s called, voluntary switchovers or some kind of post-server-crash designated survivor)
Thing is, it’ll be a long while before I’ll ever need it, and I could contribute to a generalized lightyear implementation later on, if it’s within the crates scope
What do you mean by server migrations?
A server crashes, a new one gets spawned automatically and all the clients switch to talking to that new one without loss of user data?
In that general ballpark, yeah. Also, transferring to new hosting server, or between Separate and HostServer modes, etc
Plenty of complexity, naturally.
It is currently possible for clients to disconnect from a server and connect to a new one; I guess most of the work would be:
- how to serialize the state of the World on the previous server
- load it in the new server
- update the entity-mappings on the client and make sure that the replication doesn't spawn new entities on the clients
Right, and I’ll bet there’ll be lots of unexpected cases to resolve. At the moment though, I was planning on writing my own specialized, project-specific implementation later. But, it might be better to contribute a general implementation to lightyear instead, if the author/maintainers believe that feature to be in scope
Sure that would be worthwhile; it would probably be as a separate project/plugin
Let me know if you encounter any blockers
what's the earliest schedule to render something once it's been VisualInterpolated?
right after transform propagate?
ok this is in decent shape now to merge i think. i got clippy/lints etc down to zero, but there are 4 failing tests – but i don't think they are necessarily due to my changes. https://github.com/cBournhonesque/lightyear/pull/732
i'll continue the examples refactoring in a follow-up pr later, but i switched all of them to use workspace deps so it should be easier to upgrade to bevy 0.15 in the meantime
I merged it for now, can follow up on smaller PRs. I disabled the Edgegap workflow; the other two have the message "This workflow has a workflow_dispatch event trigger."
btw the Edgegap workflow isn't from my branch.. the new ones are called "Build WASM frontend" and "Build servers for examples", which will both (harmlessly) fail on your branch at the mo so can be disabled to reduce noise.
Yep; there's just no button to disable them for some reason
@wintry dome do you get warnings like lightyear_examples_common::renderer::handle_disconnection did not run because it requested inaccessible system parameter Res<Events<DisconnectEvent>> when you run with --server?
I imagine they go away if I run with only the feature server enabled (instead of both client and server)
but it would be nice if fiddling with features wasn't necessary at runtime
I've ported the auth example, but right now it's a bit cumbersome because the user must run the examples with cargo run --features client -- client and cargo run --features server -- server which is not ideal
i put server and client in the default features, so for most people running the examples they shouldn't care about changing features and it should work like before in terms of how you launch it. feature changing only needed if you want to compile a headless server (--no-default-features -F server)
we could change the cli bit so if only server feature is on, and client disabled, assume "server" (ie, no need to pass that on the command line)
same for client. if server feature disabled we could just run the client without passing "client"
but doubt people just trying out the examples need to care about that really
i'll have to check this and get back to you. it's possible i left a system in that should be cfg'ed out (and the change to bevy 0.15 generates a new warning)
is that even necessary? could conditionally compile in server and/or client runner in based on the feature usage, dont really see any why a -- server/client argument would even be necessary in the first place 🤔 sure it means your compile time is basically double but its still more ergonomic to not have to specify server/client as a parameter
iirc cargo has feature specific cache now too, so not even iterative builds should be a problem, otherwise having different --target-dir's might make sense, i did that for an old project
Because when server and client features are enabled you could still do host-server or client-and-server
okay but thats not really a huge issue i think? compile in both client and server feature -> specify mode with additional flag like before
I don't think there's any huge issues, I just want to think about this a bit to not hurt the ergonomics of examples
makes sense; imo having default modes might make sense, server mode if we compile in server feature only, client mode if client feature and client-and-server if we compile in both. Host server mode is in my experience less useful than client-and-server so maybe having to toggle that via a flag or an additional third feature might make sense
features are pretty much the standard way to do this kinda thing in rust after all
so i'd expect this to be much more ergonomic for the avg user instead of a cli
Agreed
Is there an example, off a interpolatable/rollbackable transform component? I think I saw one here
https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/utils/avian2d.rs#L15 i think these 3 groups each need a .chain() after them, otherwise they ordering is undetermined. the outer chain only puts the 3 tuples in order. within each tuple it's undetermined. same in the avian3d plugin
if i'm right that would cause all kinds of weirdness 🤔
at least the 3 physics sets should be chained, anyway
yes but also with a chain at the end (second to last line)
edit your message to add?
).chain(),
).chain(), // <---
);
impl Plugin for Avian2dPlugin {
fn build(&self, app: &mut App) {
app.configure_sets(
FixedPostUpdate,
(
// run physics after setting the PreSpawned hash to avoid any physics interaction affecting the hash
// TODO: maybe use observers so that we don't have any ordering requirements?
(
InternalReplicationSet::<ClientMarker>::SetPreSpawnedHash,
InternalReplicationSet::<ServerMarker>::SetPreSpawnedHash,
).chain(),
(
PhysicsSet::Prepare,
PhysicsSet::StepSimulation,
PhysicsSet::Sync,
).chain(),
// run physics before updating the prediction history
(
PredictionSet::UpdateHistory,
PredictionSet::IncrementRollbackTick,
InterpolationSet::UpdateVisualInterpolationState,
).chain(),
),
).chain();
}
}```
yes. that's the same as not having the three sub-tuples and just having one chain. but perhaps the InternalReplicationSet bits could run in any order. we definitely need the physics sets to be chained though. i expect your above message was the original intention of the code
@pine cape can you comment on that? could it be a source of non determinisitic stuff with physics
oh it seens you are right, when doing sole tuple you let them run in parallel
perhaps the avian plugin is enforcing the ordering for us, i'm not sure
seems like it should inherit the ordering from the avian plugin, although we have a few places where we still use FixedUpdate. should get all the examples over to FixedPostUpdate, the avian default. #1124043933886976171 message
what's happening in the spaceships example, is lightyear adds its Avian2dPlugin (or 3d), which configures the schedules in FixedPostUpdate without chain, which doesn't matter because the schedule ordering is setup using chain() by avian's PhysicsSchedulePlugin which also uses FixedPostUpdate. the spaceships example then does app.add_plugins( PhysicsPlugins::new(FixedUpdate)... so avian chains them in FixedUpdate.
so the sets get configured in FixedPostUpdate, and then in FixedUpdate. presumable they don't run twice.. really need to change to use FixedPostUpdate anyway, that will remove some of the confusion.
how are you running examples, in this new lightyear version. Keep getting some werid certificates error
Could not deserialize the settings file: SpannedError { code: MissingStructField { field: "certificate", outer: Some("WebTransport") }, position: Position { line: 39, col: 17 } }
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
the examples are being refactored a bit at the mo since the recent commit by me in main, some of them need more work.for that error, add this to your settings.ron:
server: ServerSettings(
...
transport: [
WebTransport(
local_port: 5000,
certificate: FromFile( <---------- this bit
cert: "../certificates/cert.pem",
key: "../certificates/key.pem",
)
),
or this:
WebTransport(
local_port: 5000,
certificate: AutoSelfSigned(["localhost", "127.0.0.1"]),
),
in which case it will generate a self-signed cert each time the server starts
(i just made PR #734 to fix that)
I thought it was fine because all these sub-groups are already ordered in different places: https://github.com/Jondolf/avian/blob/v0.1.0/src/schedule/mod.rs#L75
But yes everything should be moved to the same schedule
I did what you said, I think it's not bad!
nice 💯
working on porting some examples for 0.15. are we just getting rid of ScreenDiagnostics for now?
we also don't need FixedSet::{Main, Physics} any more, now we'll be using avian's default of FixedPostUpdate. we can simplify and just put our example systems in FixedUpdate
i like the required components change