#lightyear

1 messages · Page 3 of 1

jade ember
#

Havent yet used lightyear that extensively, so have to ask to get a better Image.

Is the current approach limited to only one enum or could i define Different enums for Different categories(?) of messages

pine cape
#

The current approach is limited to 1 enum for messages, and 1 enum for components.
Prod

  • easier to work with (lightyear internals can be typed), faster iteration
    Cons
  • the user has to have a single enum; which means they can't split the protocol into different plugins
  • it requires a lot of derive macro magic
peak nymph
#

it requires a lot of derive macro magic
As a simpleton, I'd be in favor of less magic 😄

I feel like the type registry pattern is more idiomatic bevy too

bronze elm
#

Also we haven’t seen it yet, but I imagine with a large game with let’s say, 100 different components, even if this ultimately is all one enum, that is a lot more difficult to parse through and maintain. Compared to e.g. some plugin that is responsible for NPCs that directly registers its component types. Also the compiler magic with the derive macro seems fragile, like the errors can be cryptic to figure out (such as a lerp referencing something you forgot to use)

bronze elm
#

@pine cape Do you know why it's not possible to access ping_manager from the ClientConnectionManager resource? The fields are pub(crate)'d on the ClientManager struct so the error doesn't make a whole lot of sense lol. Here is the test code:

fn xyz(connection_manager: Res<ClientConnectionManager>) {
    let ping_manager = connection_manager.ping_manager;
    //  ping_manager.rtt()
}
pine cape
#

It has to be pub for you to access it, no?

bronze elm
dull lion
#

Really cool! I’ve been busy with life things the past few weeks but will be getting active again next week some time

pine cape
#

I have a big PR that changes how the protocol is defined; instead of registering the protocol at compile time with two Enums, you can register them while setting up your app: https://github.com/cBournhonesque/lightyear/pull/278

GitHub

We removed the derive macros and the protocol enums; instead we can register an enum piece-by-piece without an API that looks like:

app.add_message::<Message1>(ChannelDirection::Bidirection...

stiff quiver
#

Finally 💯

bronze elm
pine cape
#

Maybe not, since in most cases the corrector function is the same as the interpolation function. I'll just keep it for now just in case users want interpolation and correction to be different

peak nymph
#

Either way I think what you have is a big improvement

pine cape
#

yes I was thinking of doing this as well; let me try to add it

bronze elm
#

Progress update on rust in rust. Got all of the physics update issues smoothed out. The blue cylinder represents an npc, entirely server controlled physics

pine cape
#

That looks great!

#

I just released version 0.14.0! #crates message

stiff quiver
#

🎉🎉🎉

#

It looks so gooooood

#

omg

peak nymph
#

How feasible is it to write a stress test that can test lightyear (and others) performance? Something that measures packet throughput, latency, fps, etc over a range or increasing entity count would be really cool to see.

bronze elm
# peak nymph I’d be curious to hear more about what you had to smooth out and how? Movement a...

in the examples, particularly the leafwing input or simplebox, the player is spawned by the client pre-predicted, but instead I have the server spawn the player to the client on connection (e.g. add a marker component like LocalPlayer with a replicate.add_target of just Single(client_id) on the player itself

I was fighting physics for a long time with the assumption of having the camera follow the player's Predicted entity which works fine 95% of the time, but when you try doing that over the internet there is enough jitter or whatever to make it stutter randomly. The breakthrough (before almost quitting several times LOL) was to interpolate the player back to themselves (like basically interpolation_target All instead of AllExceptSingle) and have the camera translate follow their interpolated entity. You technically live on a slightly different timeline than predicted or confirmed but this is actually closer to what rust does. And also this way you don't even need to bother simulating other players (or your own) physics client side because you can just rely on the interpolation and have the server smooth you out when that packet comes through

peak nymph
#

Fwiw you can interpolate the camera itself too, to smooth out its following of the player even more

bronze elm
pine cape
peak nymph
#

I went back to my renet branch and things work smoothly even with 2000+ entities and my own interest management. So I think I’ll have to dumb down my lightyear branch and go from there.

bronze elm
#

My avoidance of pre predicted player is more to do with server initiated control, like not spawning them in until you are ready to. I think pre prediction would be great for something like projectiles but clients spawning their own player sounds weird to me

peak nymph
#

Yea agreed. I’m my case the player may influence their player model but ultimately the server should control when, where and with what items they spawn with.

peak nymph
bronze elm
#

When I started messing around with my project I went through every single networking library that's out there, and lightyear is by far the most advanced and convenient...... like no one else has even touched replication or interest management. And the fact that the transport layer is modular alone is unique. I cannot imagine using something like quinnet and having to waste time figuring out all of that stuff which detracts from the game itself

#

So I'm actually looking forward to seeing where lightyear goes, we are all early adopters lol

peak nymph
#

Totally agree, lightyear is definitely next level as far as features.

#

That said, I have a feeling some performance issues are lurking under the hood once it gets pushed harder than a handful of entities. I’m just not smart enough or proficient in Rust yet to prove it.

bronze elm
pine cape
# bronze elm in the examples, particularly the leafwing input or simplebox, the player is spa...

That's very interesting; you should have mentioned those issues you ran into, because I ran into exactly the same thing!
There is actually a solution for this integrated in lightyear: VisualInterpolationPlugin https://github.com/cBournhonesque/lightyear/blob/main/examples/replication_groups/src/client.rs#L57
If you have a component that gets updated in FixedUpdate (which is usually the case for the Predicted entity), this will visually interpolate the component value in the PostUpdate schedule.
You can enable it for a given entity like this: https://github.com/cBournhonesque/lightyear/blob/main/examples/replication_groups/src/client.rs#L142-L142
And then you can make your camera follow your Predicted entity and it will appear smooth
Some more detail here: https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html

But your solution of using interpolation even on the client owning entity is interesting; I guess the main drawback is that it adds some input delay

GitHub

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

pine cape
# peak nymph That said, I have a feeling some performance issues are lurking under the hood o...

There's definitely a few performance issues:

  • I spawn a lot of systems (usually one or more per component); this can have an adverse effect on the scheduler (although I think bevy is trying to optimize the scheduler to be able to handle more systems)
  • I think parallelization could be increased in a lot of places: optimize access by using fewer ResMut or splitting up resources, using par_iter instead of iter
  • I want to avoid having any allocation on the hot path (receiving and deserializing packets). Ideally I would just be able to deserialize the packets into pre-allocated buffers with zero-copy, but it is far from the case right now. I think in some cases the deserializing does multiple copies, even...
bronze elm
pine cape
#

Yeah, the problem is mostly that FixedUpdate doesn't run once per frame; so if you tie the camera updates to something in FixedUpdate there will be micro-stutters all the time. It was also driving me crazy, it took me a while to figure out.
But yeah the conclusion is that every visual component should be interpolated in some way or another, so that it gets updated once per frame instead of in FixedUpdate

bronze elm
#

I'm trying to upgrade to the latest commit and I'm getting this:

the package `lightyear` depends on `lightyear_macros`, with features: `leafwing` but `lightyear_macros` does not have these features.

I have these features on 'lightyear':

features = [
    "steam",
    "webtransport",
    "render",
    "leafwing",
]
pine cape
#

ah damn, did i upload the wrong version

bronze elm
#

This is with the repo, I like to live on the edge lol

bronze elm
#

oh weird, I think for some reason it was stuck on the 3990776 but i cleared out my lock file, looks like it's working now

bronze elm
#

On something like this, from the leafwing inputs example:

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

Is ok to leave off add_prediction or add_interpolation on components that don't need it? Like you wouldn't lerp a ClientId. And this is separate from replication targets right? Like, by not doing add_interpolation on PlayerId I would still get PlayerId on the Interpolated entity?

pine cape
#

You need add_prediction to specify that a component will be synced to the Predicted entity

#

But you can avoid specifying an interpolation function (no app.add_interpolation_fn())

peak nymph
#

You need add_prediction to specify that a component will be synced to the Predicted entity
does this apply to .add_interpolation() too? In other words, do you need .add_interpolation() to sync a component to the Interpolated entity?

pine cape
#

yep, same thing!

peak nymph
#

cool thanks

#

seems there are some compilation errors in the steam feature on main

#

```error[E0433]: failed to resolve: could not find wordbuffer in serialize
--> C:\Users\sqwee.cargo\git\checkouts\lightyear-2cfb5e6660946fe3\16b1c72\lightyear\src\connection\steam\server.rs:8:23
|
8 | use crate::serialize::wordbuffer::reader::BufferPool;
| ^^^^^^^^^^ could not find wordbuffer in serialize

error[E0432]: unresolved imports crate::_internal::ReadBuffer, crate::_internal::ReadWordBuffer
--> C:\Users\sqwee.cargo\git\checkouts\lightyear-2cfb5e6660946fe3\16b1c72\lightyear\src\connection\steam\server.rs:1:24
|
1 | use crate::_internal::{ReadBuffer, ReadWordBuffer};
| ^^^^^^^^^^ ^^^^^^^^^^^^^^ no ReadWordBuffer in _internal
| |
| no ReadBuffer in `_internal````

pine cape
#

Thanks! should be fixed now

#

Btw you can run cargo bench to generate some benchmark results. The benchmark is very simple now (just replicate 1 Component to 1 or multiple clients), but hopefully more can be added in the future

bronze elm
#

Or it might be something else weird. Like I feel like sometimes I'm not having Added<Predicted> happen

#

e.g. ```
Query<Entity, (With<SomeMarkerComponent>, Added<Predicted>)>

pine cape
#

Is your code public?

#

There shouldn't be any change in the order of entity spawning; but the PR was very big, it's possible that some bug got introduced

peak nymph
#

Sorry for the randomization - I'm getting a panic due to an event not be registered by the time my server starts, did I miss something?

pine cape
#

If you use pre-prediction, you need to make sure that direction is Bidirectional; also the protocol registration has to happen after adding the ClientPlugin and ServerPlugin

pine cape
peak nymph
#

you need to make sure that direction is Bidirectional
that was it, makes sense now in hindsight

#

fairly clean and painless upgrade overall, nice work!

bronze elm
# pine cape I've also seen cases where an entity doesn't get spawned; i'll have to investiga...

It's weird, it is spawning the entity but not all of the components.

protocol for ref:

        app.register_component::<NetworkId>(ChannelDirection::Bidirectional);
        app.add_prediction::<NetworkId>(ComponentSyncMode::Once);
        app.add_interpolation::<NetworkId>(ComponentSyncMode::Once);

        app.register_component::<LocalPlayer>(ChannelDirection::Bidirectional);
        app.add_prediction::<LocalPlayer>(ComponentSyncMode::Once);
        app.add_interpolation::<LocalPlayer>(ComponentSyncMode::Once);

        app.register_component::<Position>(ChannelDirection::Bidirectional);
        app.add_prediction::<Position>(ComponentSyncMode::Full);
        app.add_interpolation::<Position>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<Position>(PositionLinearInterpolation::lerp);

        app.register_component::<Rotation>(ChannelDirection::Bidirectional);
        app.add_prediction::<Rotation>(ComponentSyncMode::Full);
        app.add_interpolation::<Rotation>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<Rotation>(RotationLinearInterpolation::lerp);

        app.register_component::<LinearVelocity>(ChannelDirection::Bidirectional);
        app.add_prediction::<LinearVelocity>(ComponentSyncMode::Full);
        app.add_interpolation::<LinearVelocity>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<LinearVelocity>(LinearVelocityLinearInterpolation::lerp);

        app.register_component::<AngularVelocity>(ChannelDirection::Bidirectional);
        app.add_prediction::<AngularVelocity>(ComponentSyncMode::Full);
        app.add_interpolation::<AngularVelocity>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<AngularVelocity>(AngularVelocityLinearInterpolation::lerp);
#

this should have all of them but only have the two velocities. and the interpolated one came through fine

pine cape
#

interesting, thanks

#

does it happen all the time, or only some of the time?

bronze elm
#

only some of the time

#

so it's probably not the order like i thought, i just wasn't getting the marker component added

#

just had a run that spawned the entity with "Predicted" and nothing else lol

pine cape
#

could you please file a quick issue?

#

there must be some ordering constraint missing somewhere

#

Did you notice this only on prediction?

bronze elm
# pine cape Did you notice this only on prediction?

https://github.com/cBournhonesque/lightyear/issues/290

I'm thinking it's just on Prediction because the other ones always seem to have the data. I am going to keep testing to verify that

GitHub

With latest 0.14. I seem to get a complete Confirmed and Interpolated entity. But the Predicted entity can have all, some, or no components. On one run, I had a Predicted entity with just LinearVel...

pine cape
#

thanks

#

ah I know what it is actually

#

some system set ordering

bronze elm
#

seems only to effect predicted

pine cape
#

yes, this makes sense

#

I added Predicted based on

// get the list of entities who get ShouldBePredicted replicated from server
   mut should_be_predicted_added: EventReader<ComponentInsertEvent<ShouldBePredicted>>

I wonder if I should just base it of Added<ShouldBePredicted>

#

In general, I wonder if those replication-related events are useful

bronze elm
#

I've seen those ShouldBe____ components in the inspector but never really understood their purpose. Is it an implementation detail?

pine cape
#

it's an implementation detail yes, it's how I notify the remote that they should create an Interpolated or Predicted entity

bronze elm
#

ah ok

#

One observation is, LinearVelocity is a component in the protocol but is not being included in the spawn. Is it related to predicting components that haven't been set?

(edit: nvm i tried with adding everything in the protocol, can still have an empty entity with only Predicted)

pine cape
#

I'm not sure I understand the question

pine cape
#

I released 0.14.1 with the fix

jade ember
#

With the new runtime Configuration, you mentioned a dedicated Server that lists all lobbies, right?

Is that required? Can i manage the Server?
For example, steam offers its own lobby listing with matchmaking and everything. Could i somewhat easily integrate that?

Just currently thinking, not able to actually work on it currently. But very interested to Experiment with this later, as lightyear Looks more and more promising for my Projects

pine cape
#

Ah i think you misunderstood; I just created a new example called 'lobby' (https://github.com/cBournhonesque/lightyear/tree/main/examples/lobby) where you can run a server which replicates a list of lobbies. The list of lobbies is replicated to clients, which can join them.

So there is no lobby listing or matchmaking in lightyear, it's just part of that one example

GitHub

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

jade ember
bronze elm
#

is there a reason that RoomId uses wrapped_id and thus has u16? I didn't see it used in the protocol, it looks like it's only used for the room management. I was thinking, that if this could be wider like a u32 then it is more convenient to generate RoomIds without having to worry about conflicting IDs or some auto incremented ID wrapping. There are some clever use cases of room, that are not "client_id first" like say that some loot box has items, and you only want to replicate it when the player is close enough or has accessed it, you could put the loot box entity in its own room and then join the client into it, and kick them out when they close the interface or move too far away

pine cape
#

yeah i noticed that too recently and was confused by it; I think setting to u64 sounds good, with maybe helper functions to convert to/from entity

west garnet
#

very cool how did you make the server list visible on screen?

bronze elm
# west garnet very cool how did you make the server list visible on screen?

I have two states, GameState and MenuState. OnEnter GameState::Menu I spawn a camera2d, and a root NodeBundle that has left and right pane nodebundle children, in a percentage split. The left pane I spawn the buttons and dont really touch it. The buttons trigger a MenuState change. And then you have OnEnter for each menu state that spawns your menu contents as a child of right pane, and i add OnExit that despawns everything inside right pane. Bevy UI doesnt have tables so the server browser is just a bunch of percentage width node bundles with text inside each

west garnet
west garnet
#

@pine cape if you want to replicate a recourse bidirectional do i have to call commands.replicate_resource on both the server and the client?

pine cape
#

Yes; although I've never tested this

west garnet
#

alright i'll try it

#

it gives me this message ERROR lightyear::shared::replication::resources::receive: Only one entity per World should have a ReplicateResource<"mad_bevy_prototype::multiplayer::protocol::Players"> component

pine cape
#

Hm i see; yes I don't think it's possible currently, let me open an issue

west garnet
#

alright, then i'll have to find another solution for now tnx though

pine cape
#

You could implement it manually by exchanging Messages

west garnet
#

yeah that's what i did before 0.14 but i thought i'd try it like this

west garnet
#

I tried manual with messages but it gives me an error that the message is not part of the protocol even tho i did added the message in the protocol plugin

pine cape
#

What does your protocol look like?

west garnet
#
        app.add_message::<StartGame>(ChannelDirection::Bidirectional);
        app.add_message::<JoinLobby>(ChannelDirection::ClientToServer);
        app.add_message::<ExitLobby>(ChannelDirection::ClientToServer);
        app.add_message::<Message1>(ChannelDirection::ClientToServer);
        // inputs
        app.add_plugins(InputPlugin::<Inputs>::default());
        // components
        app.register_component::<Transform>(ChannelDirection::ServerToClient);
        app.add_prediction::<Transform>(ComponentSyncMode::Full);
        app.add_interpolation::<Transform>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<Transform>(TransformLinearInterpolation::lerp);
        
        app.register_component::<PlayerId>(ChannelDirection::ServerToClient);
        app.add_prediction::<PlayerId>(ComponentSyncMode::Once);
        app.add_interpolation::<PlayerId>(ComponentSyncMode::Once);

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

        app.register_component::<AmountOfResource>(ChannelDirection::ServerToClient);
        app.add_prediction::<AmountOfResource>(ComponentSyncMode::Once);
        app.add_interpolation::<AmountOfResource>(ComponentSyncMode::Once);
        // resources
        app.register_resource::<Lobbies>(ChannelDirection::ServerToClient);
        app.register_resource::<Players>(ChannelDirection::ServerToClient);
        // channels
        app.add_channel::<Channel1>(ChannelSettings{
            mode: ChannelMode::OrderedReliable(ReliableSettings::default()),
            ..default()
        });
pine cape
#

and you're unable to send the message?

west garnet
#

yes i do this connection_manager.send_message::<Channel1, _>(Message1{ name }).unwrap(); when pressing the connect button and i says that the message is not part of the protocol

pine cape
#

And you're using this to send from client-to-server right? I notice Message1 is ClientToServer.
In my lobby example I use bidirectinal messages so it should work

dull lion
#

I updated to lightyear 0.14.1 and now I'm seeing a disconnect event but no connect event in host server mode, which seems weird

#

I've probably done something silly

#

But it feels like it shouldn't be able to disconnect without ever having been connected

pine cape
#

No i think that's normal, it's because i start the app in Disconnected mode, so you're probably seeing logs that say "enter disconnected state"

dull lion
#

indeed

bronze elm
pine cape
#

You can hide the second type because the compiler infers it from the message you're sending

pine cape
#

@west garnet on the main branch, I enabled bidirectional resource replication. Also resource replication doesn't require the resource to be Clone anymore

#

And for send_message, you only need a reference now

#

Actually, there is no Clone bound required on any type of the protocol anymore (Message, Component, Resource) 🙂

west garnet
#

alright cool

inner hill
#

I feel like adding bidirectional replication (outside of input kinds of messages) screws a bit with server authoritative

#

Or I guess, that the networking library should push against users doing that at least a bit

pine cape
#

Yes I don't think the bidirection replication is very polished right now, also the changes from bidirectional resource replication bypass change detection to avoid an infinite loop of updates

#

But it can be convenient to have

dull lion
#

@pine cape can you tag 0.14.1 in git? I like to match my local git repo version with crates when debugging

pine cape
#

done!

dull lion
#

thanks!

dull lion
#

If lightyear is replicating two copies of a server entity to a client instead of just one, is there anything obviously wrong I'm doing?

#

Hm actually nvm I don't think that's what's happening. I'll debug some more

#

It's really weird because I'm creating players on the server only, and two players appear there. But the client somehow gets four players, a duplicate for each. I'm only seeing two EntitySpawnEvent on the client, so I'm not sure where the extra two players are getting spawned

#

I'm most likely doing something very silly

pine cape
#

The other two players that appear on the client are probably either the Predicted or Interpolated ones, they are spawned as separate entities

dull lion
#

ohhh of course

#

that would explain it 😄

dull lion
#

What is the idiomatic way to query if the current client has authority over an entity? I could match on client id and add a marker HasAuthority component myself, but this feels like something that lightyear might be already handling in some way?

#

Also sorry in advance for the coming avalanche of stupid questions but I'm finally fleshing out my networking

pine cape
#

please keep the questions coming!

#

I don't really handle authority currently; that's something I want to do (https://github.com/cBournhonesque/lightyear/issues/95) but i'm not sure of the best way to do it.
I would suggest just adding a marker component PlayerId(ClientId) or HasAuthority on the entity

GitHub

Currently the examples have an ugly "Global" resource that helps you track who is the owner of an entity. I'm not entirely sure that's needed, because the PlayerId component shoul...

dull lion
#

got it!

pine cape
#

(btw, if you have client-prediction enabled; the player you have authority will probably be the one with With<Predicted>)

dull lion
#

Is there a trick to get rust-analyzer to stop complaining about the vendor folder?

stiff quiver
#

@pine cape zstd isnt in the latest release right?

pine cape
#

no

misty carbon
#

Heyo, just came across this thread and been reading the docs. The concepts are still quite low level for me. Im completely new to game dev and would like to learn the concepts behind the ones described in lightyear, does anyone have additional resources i could read to understand more?

stiff quiver
#

he has some good articles on networking

misty carbon
stiff quiver
#

Ur welcome

pine cape
dull lion
#

I am specifying interpolation_target: NetworkTarget::AllExceptSingle(client_id), but I'm still seeing the entity with an Interpolated component on the client. Is that expected?

pine cape
#

noe

#

nope

dull lion
#

Also, why do we specify on the server-side which clients are interpolating and predicting things? Isn't this a client concern?

#

right, so I'm still doing something funny

dull lion
pine cape
#

Hm maybe you're right; the client could receive an entity, then there's logic on the client to add the prediction or interpolation

#

I guess you could do that now; listen for the ComponentInsertEvent<PlayerId> on the client, then decide based on the player_id if you want to add the ShouldBePredicted or ShouldBeInterpolated component on the client.

dull lion
pine cape
#

Yes, right now the server sends ShouldBePredicted or ShouldBeInterpolated as part of the replication message

#

but I like your approach

#

I could imagine this kind of thing be possible in bevy 0.14 once observers are available.
Something like

    if player_id = my_player {
       start_prediction
    }
);
dull lion
#

yeah, or it could just be a normal system that listens for Added<Replicate>

pine cape
#

And then checks if player_id is present on the replicated entity? maybe, yes

dull lion
#

haven't fully read up on observers yet so I'm not sure what the difference is tbh

pine cape
#

It's basically a callback that is called when a certain ECS change happens.
What's nice is that it can listen for more fine-grained events, liked OnAdd<Replicate, PlayerId> fires only if both components are added at the same time.
(in your approach, we would listen for every new replication event and then check if the replicated entity has PlayerId, which could be more expensive)

#

Also the callback happens at the next apply_deferred instead of in a different system

dull lion
#

was there any progress made on steam networking for the host-server use case? might look into that this week if not

pine cape
#

ah no i haven't tried p2p steam networking; I can't install steam on my mac so it's not easy for me to test

dull lion
#

i'll probably try to pick it up then!

#

as soon as i've solved some catastrophic physics bugs lol

pine cape
dull lion
#

btw what sort of monitoring stack do you run when testing? i see you've put some effort into observability

pine cape
#

I just update the env_filter in the log plugin to enable the logs I need (lightyear::client::prediction::rollback = trace), but nothing much more than that

dull lion
#

got it, thought it might be something fancier

pine cape
#

I wanted to log metrics to prometheus/grafana but there's a bug in bevy's LogPlugin that prevents me from doind that. Maybe in 0.14

dull lion
#

yeah that would be neat

west garnet
#

I tried to add a pbr mesh to the player entity on the client side by using commands.entity().add_child but is gave me warnings saying that the parent didn't have an inheritedVisability component. Then i tried adding the visibility components to the playerbundle and the protocol but they don't have the serialize trait. Is there another way to add a pbr mesh to the player entity on the client side?

pine cape
#

I think you can just add a PbrBundle on the client side, which contains Visibility and InheritedVisibility

west garnet
#

yeah i did that but then it gave the warnings about the player entity not having Visibility and InheritedVisibility

dull lion
#

visibility should probably not be in the protocol since that just controls how the client renders things

pine cape
#

As martin said, visibility is probably purely on the client side

dull lion
#

Is there any reason you want to add a pbr bundle as a child of something which isn't rendered? can you just add the bundle directly onto the entity?

pine cape
#

ah right, that's the problem

west garnet
#

yeah the problem is that the pbr mesh is a child of the playerbundle i made but i don't know how to add the pbr bundle directly onto the entity

dull lion
#

commands.entity(my_player_entity).insert(PbrBundle { ... })

west garnet
#

alright tnx i'll try that

bronze elm
dull lion
#

what's the easiest way to inspect network stats? I see that there is a diagnostics plugin but it seems to be private

pine cape
bronze elm
#

is it possible to modify the Replicate network target after the fact? e.g. initially have All but change it to AllExceptSingle later on?

pine cape
#

Hm i'm not 100% sure.
I think it would work if you removed the Replicate component and then re-added it with AllExceptSingle, but I don't think I handle updates right now

pine cape
bronze elm
#

Ok, that makes sense. Wasn't sure if Replicate was set in stone once added. I think rooms are a better solution for that.

dull lion
#

I accidentally added a link conditioner on the server io config instead of on the client, and this seems to make the client very jittery. Is this expected?

#

Putting the link conditioner on the client seems to work much better

dull lion
#

actually no, things are very jittery still

pine cape
#

It shouldn't make a difference, link conditioner works for the packets you receive, so if you put it on the server all the packets received by the server will have some delay/jitter/loss added

#

Things are jittery? What are you replicating, physics?

dull lion
#

Yeah I'm not sure what I was doing because there now seems to be no difference between the two. I'm replicating physics with xpbd

#

Everything looks fine on the server but the client's predicted entity is very jittery. I'm not entirely sure how to debug. Now I'm just trying to go over every line comparing with the examples

pine cape
#

Some common issues:

  • does it look good with one player only?
  • ordering of inputs and physics (input handling should come before the physics update)
  • the leafwing input data shouldn't be replicated back from the server to the client
  • are all the components that impact the position present in the ComponentProtocol with prediction enabled?
  • is client -> server replication enabled if you use pre-predicted entities?
dull lion
#

So I have one host-server and a client. Host-server works perfectly no matter what (which is unsurprising I guess), although I never tried putting link conditioning on the host-server's client plugin. Adding a remote client makes it break down on that client

I will try verifying the things you mentioned

dull lion
pine cape
#

@bronze elm made the example; it was broken, but I helped him fix it. It should just work now

bronze elm
#

I will suggest maybe only syncing Position and Rotation and not Transform, because xpbd itself syncs those, and trying to replicate Transform causes some strange issues. The jitter can be reduced by tweaking the system order (or you can do something weird like I did and interpolate the player back to themselves)

pine cape
#

@bronze elm you shouldn't have any jitter even with prediction no? (in the example). It worked for me

bronze elm
pine cape
#

I have an issue where my incremental compilation is semi-broken, every time i run it recompiles ~50 packages, which is super annoying. Do you also have this issue?

peak nymph
pine cape
#

yes, it's infuriating

#

Looks like bevy and wtransport are being recompiled. Maybe it's because of the wtransport upgrade? I will try to bisect it

#

@peak nymph does it recompile the same packages for you as well?
Mine usually starts with

Compiling objc_exception v0.1.2
 Compiling blake3 v1.5.1
 Compiling ring v0.17.8
 Compiling zstd-sys v2.0.10+zstd.1.5.6
 Compiling objc v0.2.7
dull lion
#

I've had this issue in the past due to a broken rust-analyzer version I think

#

my jittering issue is almost certainly a system ordering problem

#

just need to figure out what is misordered

pine cape
#

I fixed my incremental compilation issue... I had some compiler env variables in a tmux tab that were causing recompilations..

peak nymph
#

Mine may be a tmux env var issue too, will have to check. I know I had some stuff set while doing some profiling a while back.

#

But yes I did notice those packages being rebuilt every time. Ring was definitely on the list

west garnet
pine cape
#

Ah i see!
Yes you can use the run_condition is_connected to check if the client is connected. Or check if the NetworkingState::Connected

west garnet
#

yeah that is what i'm doing now 🙂

peak nymph
pine cape
#

I think xpbd already syncs positions to transforms

peak nymph
pine cape
peak nymph
pine cape
pine cape
#

I just merged 2 big changes (cc @floral meteor @dull lion )

  • I removed the
  enable_send: bool,
  enable_receive: bool
}

It was easy to misuse. The original goal was to enable/disable the replications systems at runtime, but instead it was used to not spawn replication-send systems on the client side to save CPU.
Instead, the ClientPlugin and ServerPlugin have been replaced with ClientPlugins/ServerPlugins which are PluginGroups. This means that you can freely disable any of the subplugins in lightyear (Rooms, ReplicationSend, ReplicationReceive, Prediction, etc.)
All the plugins are enabled by default, but users who don't want to use a subset of features can just disable the corresponding plugin. (PR)

  • I updated all examples to use a common test harness. (PR)
    Hopefully this should make the examples clearer to read. All the complicated code destined to showcase the multiple modes (ListenServer, HostServer, etc.) and transports (WebTransport, Steam, etc.) now lives in a separate common crate. The example folder only contains the code that is actually relevant to the example
dull lion
#

great job!

dull lion
#

Am I supposed to have an ActionState on both predicted/interpolated and confirmed entities, just one of them, or does it not matter?

pine cape
#

The ActionState should be on the predicted entity

dull lion
#

FixedUpdate can run zero times in a frame right? So it's possible for lightyear to replicate a Position to a client, and then FixedUpdate doesn't run that frame, and so Transform is not synced during that frame (if xpbd is running in FixedUpdate)

#

Or am I mistaken

bronze elm
#

I think that sounds right

pine cape
#

Yes that's correct

dull lion
#

Okay I think I've worked out my transform sync issues. Now I'm trying to work out why prediction is not working the way I expect it to.

I have a stupid character controller which just applies some linear velocity in the direction I press. Here I'm just holding down the up key, and the confirmed entity (in red) seems to settle in the ceiling which is what I would expect, but the predicted entity (green) is getting pushed down by gravity each frame, never reaching the ceiling.

The ActionState of the predicted entity consistently shows that the up key is being held, and I've copied the FixedSet::Main/Physics split from the leafwing example, applying inputs in FixedSet::Main. So I'm somewhat confused as to why it seems to consistently predict downward motion

#

The confirmed entity seems to consistently have a small amount of downward velocity. Since the predicted entity is a few frames ahead, I guess this small error then gets magnified

#

So maybe this is more of a problem of 1) my character controller is dumb and 2) bevy_xpbd doesn't reach an equilibrium here

pine cape
#

Cool art!

#

Maybe you can invite me to your repo privately so i can take a look? I can't immediately tell right now

dull lion
#

I invited you! It might crash and burn, as it's not very well tested on other computers 😄 I don't expect you to fix my code for me but feel free to have a look

dull lion
#

VisualInterpolationPlugin seems to create tons of rollbacks even if the previous value is very close to the current value. Perhaps it makes more sense to instead let the user provide some approximate equality and check that |current - previous| > epsilon

#

might also be useful for rollback in general?

pine cape
#

Yes, I think letting the user define a comparison function could be useful

dull lion
#

and due to orphan rules this has to be a function pointer somewhere

pine cape
#

yes, unfortunately

#

VisualInterpolation should not contribute to rollbacks, because in PreUpdate we restore the actual value of the component

dull lion
#

it does affect rollbacks for me though

pine cape
#

hm i see, there's an ordering missing. The component restore should happen before the rollback check

pine cape
#

For some reason I cannot observe this in the replication_groups example, where I have prediction + visual interpolation enabled. (i.e. I don't see any rollbacks)
I'll still do the ordering change, I would have liked to be able to confirm the behaviour though

dull lion
#

does not seem to have solved the issue for me. i'll have a look

pine cape
#

Could it be your position quantization scheme?

mighty mason
#

Is lightyear constantly replicating every entity or does it only replicate change in entities or how does that work?

pine cape
#

It only replicates entities that have the Replicate component and it constantly replicates updates for that entity's components that are present in the ComponentRegistry

dull lion
dull lion
pine cape
#

The conditions should be correct, rollback_check_fn is basically PartialEq::eq;
it seems to work for me in the examples

dull lion
#

It should return true if you should roll back, no? which would be the opposite of equality

pine cape
#

hm maybe i should change the name to equality_fn, the function you pass in should just be is_equal?

#

it should return true if no rollback is required

dull lion
#

yeah i think that's a better name

#

the good news is that it does seem to reduce a bit of rollback noise for me

pine cape
#

But tbh there really shouldn't be any rollbacks with only 1 player

dull lion
#

only one remote player you mean?

pine cape
#

yes

dull lion
#

hmm yeah. food for thought 😅

pine cape
#

the client should more or less do a perfect prediction since it has all the same physics rules as the server

dull lion
#

it does predict physics perfectly for me if i don't provide any inputs, and just let my character bounce down through the cave

dull lion
#

So I'm running some logging in FixedPostUpdate.

  1. On Tick(327), I start moving right. But the LinearVelocity component on my player is still 0, and it remains 0 for many ticks after that.
  2. Lightyear starts a rollback on Tick(328), but that is one tick after I started moving. If it wants to roll back I think it should start the rollback on Tick(327)
  3. After the rollback it resumes on Tick(329) with a non-zero LinearVelocity
#

Ah damn it, pressed enter too early

#

I think the effect of this is that it skips two ticks though, because I was moving right on tick 327 and 328 but it only starts registering the new velocity on the rolled-back tick 329

pine cape
#

Can you show me the actual logs?
Lightyear starts rolling back one tick after, because on the tick of the rollback check it already sets the correct component value, so there's no need to rollback from that tick again.
The tick where it actually starts the rollback is the one from the line: https://github.com/cBournhonesque/lightyear/blob/5239752b40f4da9ad56346c434d6a144a0403977/lightyear/src/client/prediction/rollback.rs#L185

GitHub

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

dull lion
#

I'm a little bit confused about why linear velocity remains 0 for so many ticks

pine cape
#

when is your player_sim log? In FixedUpdate after the FixedSet::Main? Or after the movement system?

dull lion
#

In FixedPostUpdate, after player inputs

#

really just doing this:

fn debug_stuff(
    tick_manager: Res<TickManager>,
    rollback: Option<Res<Rollback>>,
    player_q: Query<
        (&LinearVelocity, &PlayerSlot, &ActionState<PlayerAction>),
        (With<Player>, With<Predicted>),
    >,
) {
    let tick = rollback.map_or(tick_manager.tick(), |r| {
        tick_manager.tick_or_rollback_tick(r.as_ref())
    });
    for (linvel, slot, action) in player_q.iter() {
        if slot != &PlayerSlot(1) {
            continue;
        }
        info!(
            "tick: {:?}, player vel: {:?}, moving right: {:?}, moving left: {:?}, ",
            tick,
            linvel,
            action.pressed(&PlayerAction::MoveRight),
            action.pressed(&PlayerAction::MoveLeft),
        );
    }
}
mighty mason
#

I'm really having a difficult time understanding protocols

#

I don't really understand what the protocol function is and it has a red outline whenever I try to use the protocolize!

pine cape
#

Are you using the latest version? the protocolize! macro doesn't exist anymore

peak nymph
pine cape
#

I had AR and CC set

peak nymph
#

okay I have those pointing at a homebrew install of clang, didn't realize it could impact which packages get recompiled

bronze elm
#

kind of a random idea... what if there was a way to configure replication (sort of like how replicate_once works) so that you could add Replicate to a component and have it spawn client side, but otherwise doesn't get any updates, but then the server could trigger a replication later on.... Right now the choice for something that's "request-response" is 100% message, or client initiated message with server replicating back, but the message route the convenience of having stuff magically sync is lost and with replication packets are sent all the time and trigger ComponentUpdateEvent when there are no changes. Also messages don't respect rooms, so that would be another advantage of this method following interest management out of the box

wicked tulip
#

Does lightyear potentially work with physics?

pine cape
#

Yes it does, check https://github.com/cBournhonesque/lightyear/tree/main/examples/leafwing_inputs for a 2d example with xpbd, and https://github.com/panjeet/networked_cube_test for a 3d example with xpbd and tnua

GitHub

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

GitHub

Contribute to panjeet/networked_cube_test development by creating an account on GitHub.

pine cape
pine cape
bronze elm
#

Also it would be great to be able to send a message to a room, the client hashset is not public so you have to do something like this

     let client_ids: Vec<ClientId> = server_global
            .client_id_to_entity_id
            .clone()
            .into_keys()
            .filter(|client| room_manager.has_client_id(client, room_id))
            .collect::<Vec<ClientId>>();

which is not ideal when room already has the list 😄

pine cape
# bronze elm Yes which is what I've tried, but it constantly is replicated even when updates ...

I don't think it's constantly replicated, it should only be replicated when the component has an update.
But yes there are some issues:

  • replication updates always trigger change detection, even when the component is the same. (I could prevent this but it would required adding a PartialEq bound on the component)
  • we keep sending an update until we have received an ACK from the remote. issue This means that we potentially send the same update many times, because it takes time (1 RTT) to receive an ACK. This is probably why you're seeing 'constant replications' even when you do a single update. I plan to fix this
GitHub

Joy — 12/31/2023 3:48 PM on Discord traditionally the server maintains some bookkeeping per-entity, per-component (sometimes per field/property), i.e. it'll remember the last tick it sent an (e...

bronze elm
#

oh, that makes sense... is there any workaround to the first one? the event only has the entity so i dont know how I would compare them

pine cape
#

You're not doing bidirectional replication using a same entity, right? you're using 2 entities?

#

That would trigger an infinite loop of replication, since every message received triggers change detection, which triggers a new replication send

#

I don't know if it would be useful, but you could use bidirectional Resource replication? I've disabled change detection for bidirectional resource replication to avoid this specific problem.

bronze elm
#

eh... a simple analogy to illustrate is say you had a light in a room, it rarely changes state, but if a player clicks on a switch (i.e. client sends message saying im clicking on an object), the server changes the light state, for all players in the viscinity that get that replicated update, say i want to take that boolean and do something with it only when that state changes

pine cape
bronze elm
pine cape
bronze elm
#

Yeah the only reason I'm trying not to is that entity is already using rooms, so I would have to handle spawning and despawning myself with messages

pine cape
#

i'm not sure what the exact usecase is, but you could try just moving entities in/out of rooms? that will make the server send spawn/despawn replication updates

bronze elm
#

I guess this would very nearly work, the only thing I can't detect is when there is an actual change on the client side

bronze elm
pine cape
#

maybe you could send me a code example in private? I have a hard time understanding

dull lion
#

I'm looking at the steam integration now, and there are a few things I want to change:

  1. I want to run run_callbacks outside of lightyear, through bevy_steamworks.
  2. I also want to start the steamworks client outside of lightyear (also via bevy_steamworks)
  3. I don't think I need a steamworks::Server for P2P connections, so this would have to be done separately for dedicated servers
    And probably more stuff as I work on it!

I can upstream these changes to lightyear, or I'll fork the steam integration and do the changes myself

dull lion
#

I'll fork the steam integration and do the changes myself
This is maybe not even possible since there is a match statement in connection/client.rs which links NetConfigs to implementations

dull lion
#

This now feels like a slightly bigger refactor than I was hoping for... To use the steam client provided by bevy_steamworks, I'd have to access it through the ECS resource it provides. However, it seems that lightyear expects client networking functionality to be fully contained within the ClientConnection struct and there's no way to pass in additional data from the ECS

#

From the looks of it, this is handled in Renet by registering send/receive systems from the steam plugin which looks nicer on the surface level since it 1) allows transport plugins to be provided from outside of the library 2) allows arbitrary ECS queries to be run in the plugin's systems

pine cape
dull lion
#

in general, though i'm mainly interested in p2p

#

it just feels like this shouldn't be in lightyear at all since other steam feature like e.g. achievements have nothing to do with networking

pine cape
#

I wanted them to be in lightyear to have an easy hands-off integration without having to worry about calling the callbacks, etc. yourself.
I think we could just add some fields run_callbacks: bool and client: Option<steamworks::Clien> in the SteamConfig. If no client is provided, we create our own

#

In which case you could just use the client however you want outside of lightyear

dull lion
#

sure, that's alright with me. but it still seems a little bit awkward to pass in a Client/SingleClient into the NetClient since it has no ECS access

pine cape
#

But yes, I acknowledge that having the ClientConnection hide the internals behind a trait object can be annoying

#

This way you would still get access to it via the ECS

#

i'll think about it

dull lion
#

I don't think that works for SingleClient though, since it is not sync

pine cape
dull lion
#

Hmm interesting

dull lion
pine cape
#

nice! maybe james will take a look, I think he has push access to that repo

bronze elm
pine cape
#

I made it pub in main 🙂

mighty mason
#

I was copying one of the examples and using the component insert event and was only getting an empty value in the context of the event as opposed to the value

bronze elm
#

Update on rust in rust... implemented inventories using lightyear replication + interest management. Each inventory is spawned server side and put into its own room, and the client is enjoined to allow it to update. This way if multiple players are looting the same crate for example they will all get updates. Then on front end whenever the inventory updates it gets synced to a separate set of UI components

pine cape
pine cape
mighty mason
#
Resource requested by server::network::replicate_players does not exist: bevy_ecs::event::Events<lightyear::shared::events::components::ComponentInsertEvent<common::player::PlayerId>>
pine cape
#

Did you add the PlayerId component to your protocol? With the correct direction?

dull lion
#

I am so confused. I am getting this error message right after initializing the steam client:

2024-05-12T15:38:12.397021Z  INFO lightyear::connection::steam::server: Steam client initialized
[S_API FAIL] Tried to access Steam interface SteamNetworkingSockets012 before SteamAPI_Init succeeded.
#

The renet example doesn't do anything apart from calling steamworks::Client::init_app which is what I'm doing too

#

and yet the renet example does not seem to crash 🤔

#

client.networking_sockets() is not null right after initializing the client, but is null when the server is starting

pine cape
#

In your branch, or in main?

dull lion
#

My branch

#

I guess no use in publishing it since you can’t test it!

pine cape
#

I think it would still be useful, so that I can look at the changes

dull lion
#

Gonna have some food and then i’ll be back

mighty mason
pine cape
mighty mason
#

I'm using whatever the most up to date crate version is

pine cape
#

For client->server replication, you need to enable it in the ReplicationConfig for the client and the server; it's not enabled by default (because 95% of cases just need server->client replication)

mighty mason
#

Oh ok

#

thank you I didn't even realize this was a thing

dull lion
#

Perhaps the server should have a similar static OnceLock thing as the client to prevent accidents

#

but I should figure out where the second call is coming from

#

Indeed it looks like we create the server both when adding the plugin and on start:

// Stack trace of first creation
lightyear::connection::steam::server::Server::new
lightyear::connection::server::NetConfig::build_server
lightyear::connection::server::ServerConnections::new
lightyear::server::networking::rebuild_server_connections
bevy_ecs::system::system::RunSystemOnce::run_system_once
<lightyear::server::networking::ServerNetworkingPlugin as bevy_app::plugin::Plugin>::build
bevy_app::app::App::add_boxed_plugin
bevy_app::plugin_group::PluginGroupBuilder::finish

// Stack trace of second creation
lightyear::connection::steam::server::Server::new
lightyear::connection::server::NetConfig::build_server
lightyear::connection::server::ServerConnections::new
lightyear::server::networking::rebuild_server_connections
lightyear::server::networking::on_start
pine cape
#

I don't think this can be removed currently; but in your branch the steamworks::Client would be contained in a separate Resource, no?

dull lion
#

I removed the steam client resource logic thinking I could skip it for now, but now I'm back trying to refactor the steam client logic again

#

I'll try to make server creation possible to run multiple times like the client

pine cape
#

If you're blocked on some refactoring or some config thing, don't worry too much about it.
As long as the core p2p part works I can move things around afterwards

dull lion
#

Okay, I pushed a change which fixes the issue for me. I can now create the steamworks client outside of lightyear and pass it in

#

I used Arc<RwLock<...>> which I guess should actually be unnecessary if the single client is actually sync

#

But this seemed like an easy way around the problem for now

#

The old behavior should be preserved since you could just pass None into the steamworks_client field, but I actually think this is a little bit ugly and the option should maybe be removed entirely?

#

This should also allow client and server to use the same steam client in host-server mode

pine cape
dull lion
#

I could add a function to create a default, without needing to add steamworks as a dep to your project

#

Or wait, this is already in the pr

dull lion
#

I think I need to wait for my steamworks account to be approved before I can make any further progress

pine cape
#

Sounds good, thanks for the work so far!

dull lion
#

I'm getting an InvalidHandle error both in lightyear and renet which I'm assuming is because my account is not approved

pine cape
#

yes I had to wait for my steamworks account to be approved as well

mighty mason
#

Just curious how easy it would be to switch from not using steamworks to using steamworks?

pine cape
#

Very easy. All the examples already are compatible with steamworks by default; you just need to uncomment the lines

GitHub

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

mighty mason
#

Oh sweet that’s great

jade ember
pine cape
#

I've merged a big PR that introduces a number of changes:

  • Replicate is not a Component anymore. It is now a Bundle of multiple smaller components that each control an aspect of replication:
    • ReplicationTarget to specify who the entity should be replicated to
    • VisibilityMode to enable interest management
    • ControlledBy so the server can track which entity is owned by each client
    • ReplicationGroup to know which entity updates should be sent together in the same message
    • ReplicateHierarchy to control if the children of an entity should also be replicated
    • DisabledComponent<C> to disable replication for a specific component
    • ReplicateOnceComponent<C> to specify that some components should not replicate updates, only inserts/removals
    • OverrideTargetComponent<C> to override the replication target for a specific component
  • Before, the Replicate component was created once and could not be updated after creation. It is now possible to update these replication components at runtime (for example to update the ReplicationTarget of an entity)
  • Added 20+ tests around the replication logic, so hopefully everything should be air-tight
GitHub

Big refactor of the replication logic
Fixes #329

Replicate is not a Component anymore. It is now a Bundle of multiple smaller components that each control an aspect of replication:

ReplicationTar...

#

There's a couple of other things I want to fix (Issue1, Issue2), and then i'll probably push a new release.
This will be the last release before bevy 0.14

GitHub

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

GitHub

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

dull lion
#

good stuff!

dull lion
#

Hmm, I'm trying to use leafwing's just_pressed in FixedUpdate but it is missing key presses. This does not happen when running the same system in Update. Is this something you have encountered?

#

I wish I could search threads on discord

pine cape
#

yes, it's something I've encountered, I wish I had a better solution for it

#

I think the solution will be to have better support from Leafwing (for example also run leafwing systems in FixedUpdate)

dull lion
#

haha // NOTE: pressed lets you shoot many bullets, which can be cool silver lining

#

should I use the lightyear::client::input::InputSystemSet::BufferInputs system set with the leafwing plugin too?

#

consuming the action and using pressed does seem to work

#

not sure if there are any caveats here

pine cape
#

Pre-requisite knowledge:

  • JustPressed becomes Pressed in a system that runs in PreUpdate (so once-per-frame)
  • lightyear handles inputs in FixedUpdate, to have access to tick information

Basically the problem is that:

  • if you use just_pressed in FixedUpdate: you can have frames where FixedUpdate doesn't run, so the JustPressed becomes Pressed and just_pressed() misses the input
  • if you use just_pressed in Update: I actually cannot remember the exact problem here.. I think it's that the tick-number in Update is not reliable. There's no guarantee that it would be the same as in the server, so you might get off-by-1 errors.

The solution to use pressed in FixedUpdate and consume the action. I think it works, but it generates more diffs than necessary (wasted bandwidth) because of some leafwing issue

#

But basically this is a huge footgun! Maybe i'll push a fix to leafwing directly, as this is super important

dull lion
#

it's certainly quite confusing

pine cape
dull lion
#

hm, so should input handling be in FixedPreUpdate or FixedUpdate? the examples use both

pine cape
#
  • without leafwing: you have to buffer the inputs manually yourself, and it should be done in FixedPreUpdate (in BufferClientInputs).
  • with leafwing: you don't need to buffer inputs manually yourself, it's done for you in FixedPreUpdate.

In both cases you should consume the inputs in FixedUpdate

stiff quiver
#

works flawlessly with tab switching

#

question is now how to actually integrate this

#

@stiff dome do you know what wasm-bindgen-futures exactly does for spawn_local?

#

because to me it doesnt seem like it uses promises

#

async timer seems to just use setTimeout so that might be whats keeping it alive

#

but prob not

#

huh

#

oh yea nvm

#

just didnt look for the right stuff

#

@pine cape so yea promises seems to work and maybe this weird queueMicrotask might also be an interesting thing to explore

pine cape
#

i'm out of my depth here haha, I don't really understand what you're saying.
The xwt examples work cross-tab, so how did you identify that requestAnimationFramework is causing the issue in lightyear?

#

What is the difference between the xtw examples and the lightyear examples?

#

It uses wasm_bindgen_futures::local directly instead of IoTaskPool::spawn_local?

stiff quiver
#

bevy uses rAF to run its systems and pools

#

including the io pool

#

meaning it will stop running shortly after the user switches tabs

pine cape
#

are there docs about bevy using the rAF?

stiff quiver
#

idt so no

#

its just the main way of using vsync in the browser

#

everything else doesnt really work and causes frame lags and so on

pine cape
#

The client still gets disconnected on tab change (testing with simple_box)

stiff quiver
stiff quiver
#

working demo, couldnt get the example to run with trunk directly

#

u would need to replace the cert with the one generated by the server tho

pine cape
#

Yea I trust that the xwt examples work, but in lightyear I just get this error Error(JsValue(WebTransportError: Connection lost.Error: Connection lost.))

stiff quiver
pine cape
#

on websocket, i get client connection timed out from the netcode side, which means that we have stopped receiving keepalives

stiff quiver
#

I'd like to check out rn but

#

this been going on for a while 😂

stiff quiver
pine cape
#

thanks for testing the xwt examples! at least we're making progress

stiff quiver
#

yea

#

i wish i saw mozgiis reply earlier

#

:x

#

hm @pine cape there might be a possibility that tokio is actually at fault here

#

prob not but its like one of the few differences between the xwt example and this

pine cape
#

I don't use tokio in wasm though, apart from their oneshot channel

stiff quiver
#

although

#

hm

#

there is another possiblity that might be more annoying to fix

pine cape
#

ah maybe; i can replace it with a futures::select

stiff quiver
#

what happens if the packet recv channel keeps on receiving but not getting cleared because raf/bevy isnt running

#

lightyear might think the client is timeouted because it doesnt send back keep alives

pine cape
#

yes i guess it doesn't matter if I use wasm_bindgen_futures

#

the problem is still that the bevy app stops running because of RAF

#

so we are not sending back packets

stiff quiver
#

yea

#

welp that sucks

#

i have no idea how to even approach fixing that

#

running bevys main loop independant of RAF doesnt make sense

#

maybe having netcode outside of raf

pine cape
#

I could have a separate thread in wasm_bindgen_futures::spawn_local

#

that keeps returning keepalives

stiff quiver
pine cape
#

[Violation] 'requestAnimationFrame' handler took 52ms

#

WebTransport connection closed. Reason: Err(JsValue(WebTransportError: Connection lost.Error: Connection lost.))

#

I tried disabling Energy Saver and Memory saver on chrome but i still get disconnected

stiff quiver
#

as long as it doesnt happen all the time

stiff quiver
#

if not it does instantly

pine cape
#

actually weird that I got a client connection timed out from netcode in the websockets case

#

since from our understanding the bevy threads shouldn't be running at all?

stiff quiver
#

yea

stiff quiver
#

raf will just indefinetly wait to run yourfunc again while ur not on the tab

pine cape
#

I see, so when I come back to the tab, netcode logic runs again, it sees that the last packet receives was 5 seconds ago and errors with timed out

stiff quiver
#

i guess so

pine cape
#

So i guess solutions are:

  • run a separate thread outside of BevyIoTaskPool that keeps receiving packets and sends keepalives. When we're back, send all the buffered received packets to the bevy task (i.e. the bevy task reads from the channel)
  • put all of netcode outside of bevy systems? seems hard
  • play audio (needs to be tested)
stiff quiver
#

seems logical

#

imo 1 is prob best solution

#

since we only need this on the web client, we could also just do this entirely in js

#

altough prob not worth it

#

since xwt does the js part for us already

pine cape
#

not worth it, the keepalive packets need to be encrypted and the whole logic is already in rust

stiff quiver
#

ah alr

stiff quiver
pine cape
#

yea I think sending just sends keepalives, since we have nothing to send

#

recv would probably be bounded in practice; we don't want to buffer an infinite number of packets

#

but honestly the best solution would just be for bevy to be able to keep running in the background

stiff quiver
#

does it get rid of the old or new packets tho

#

when bounded

pine cape
#

I think we can decide

stiff quiver
#

ok

stiff quiver
pine cape
#

it could just run in headless mode

#

like not generate frames

stiff quiver
#

hm

#

maybe lets make an issue for that then

#

i wonder if request idle callback still runs in background

pine cape
#

I'm not knowledgeable enough for that, i still don't know what the RAF is. I asked a question in #web

stiff quiver
#

might be a headless alternative to that

pine cape
#

this seems relevant: #web message

#

I don't think 1) is even a good solution, because you would have to process potentiall 1000s of frames at once on the client when you connect the tab again

stiff quiver
pine cape
#

universal.
If you have 2 predicted entities that are in different server ticks (because you receive their updates in separate messages):

  • E1 in tick 12
  • E2 in tick 14
    And you notice a rollback for the one at tick 14 (because the value is different from the predicted history)
    Then we need to rollback everything.
    But we cannot rollback from tick 14, since we don't know yet what the value of E1 in tick 14 is
stiff quiver
#

like lets say we have two players each have a child, do all entities need to have the same group

pine cape
#

In practice it's not a problem since you only need predict your own entities. (so there's not that many entities that will be predicted/in the same replication-group)

stiff quiver
#

which would necessitate predicting other players entities i think

#

might be totally off idrk

pine cape
#

I guess you could actually rollback from the oldest received tick of any predicted entity, which would be tick 12

#

This means that for predicted entities, we need to store the history of all server received ticks (up to the oldest received tick of all predicted entities). i.e. for E2 we also store what the state at tick 12 was

#

but it could be problematic..
What if the update for E2-tick12 hasn't arrived yet? or is lost? then I guess you cannot rollback properly

#

but maybe this can be an alternative to be investigated

stiff quiver
#

oh ig i misunderstood :v

dull lion
#

Could be convenient to have a ComponentUpsertEvent to have one event type for all modifications to a component, what do you think @Periwink?

pine cape
#

I think I will remove those events as soon as bevy 0.14 lands, as it will be more appropriate to use observers to react to them

#

|Observer<OnAdd, (C)>, Query<(&C), With<Replicated>>|

#

(so that i don't have to emit events everytime even though they are not consumed, which wastes CPU for nothing)

#

We only have OnAdd OnRemove OnUpdate observers; I think OnUpdate is what you call upsert

dull lion
#

got it, makes sense

stiff quiver
#

yea that makes sense

dull lion
pine cape
#

nope, probably can be removed

pine cape
#

it looks like players are also pre-predicted

#

bullets are pre-spawned 🙂 confusing i know

dull lion
#

Hm, is there a difference between pre-spawning and pre-prediction?

pine cape
#

Pre-prediction is client-induced: the client spawns an entity, then replicated it to the server, which takes authority.

Pre-spawning: a shared system runs on both client and server (for example 'spawn bullets'). It will run first on the client (since it's a bit in the future). When the server spawns the bullet and replicates it to the client, it will use a hash to find the existing pre-spawned bullet on the client and use it as its target.

dull lion
#

ah, alright!

jade ember
#

@dull lion how is it going with the steam p2p changes?

If there is anything i could take up, just let me know. Would gladly help. Got some time atm

dull lion
#

i'm not working on it at the moment as i'm still waiting on steam to get back to me. I implemented the changes I think are necessary in the draft pr, except for tests/docs etc

#

feel free to test the pr in your game

#

maybe it works, maybe not!

jade ember
pine cape
#

thanks for the identity fix @dull lion 🙂
I'll release 0.15 now

pine cape
#

Released! #crates message

stiff quiver
#

Awesome 🎉

jade ember
#

@pine cape i see that the settings.ron isnt really a lightyear thing, and more something you built for the examples.
I think it would be really cool to have that directly in lightyear, so anyone could use it. maybe we can even add hot reloading, allowing us to change config at runtime.
opinions?

pine cape
#

Hm i'm a bit hesitant to include something like this that is pretty independent from the core of lightyear.
There are probably some dedicated hot-reloading crates that could handle this better

stiff quiver
#

yea seems super out of scope

jade ember
dull lion
#

Probably just a leftover debug log

jade ember
#

yea i figured. was just very confusing as a first time user, thought something didnt work

pine cape
#

ah yes, just a forgotten log

#

you're running in host-server mode?

west garnet
#

i want a certain component cloned from client to server, do i have to insert that component on the client side or the server side?

#

and does it automaticly get cloned when it gets inserted?

pine cape
#

You have to insert the component on an entity on the client side, and also add the replicate bundle on the entity.
Like this: https://github.com/cBournhonesque/lightyear/blob/main/examples/client_replication/src/protocol.rs#L24

GitHub

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

west garnet
#

i spawn the player bundle on the server because al the other components need to be server to client, does it work if i do commands.entity(entity).insert(Component) on the cloned entity that is on the client side?

pine cape
#

So you want:

  • to replicate an entity from server to client
  • but to replicate one specific component from the client entity to the server?
west garnet
#

yes

pine cape
#

I think the easiest might be to just send a message from client to server, and when the server receives it you insert the component

#

Otherwise I haven't tested it, but you might be able to:

  • add server::Replicate on the server_entity S to start replicating to the client
  • then add client::Replicate on the client_entity C to start replicating to the client. You would need to include DisabledComponent for all the other components to avoid their replication.
  • then you can add the special component TargetEntity to specify that you don't want to spawn a new entity on the server, but re-use the existing entity S.
  • On the client, you have access to the entity S via ConnectionManager.replication_receiver.remote_entity_map

Two things come to mind:

  • are you sure you need this? it is a bit unusual. What is your use-case?
  • I think I might need a more expressive way of specifying which components need to be replicated. There's no way to specify ReplicateOnly<C> to only replicate a single component
west garnet
#

alright, i think i'll have to rethink my structure, i need i specific transform form a client side entity (that doesn't exist on the server) when i press a button. I tried doing this with a message but the message with the transform information doesn't come in time for when the input is processed.

#
    mut input_reader: EventReader<InputEvent<Inputs>>,
    mut message_reader: EventReader<MessageEvent<PlayerTransform>>,
    mut commands: Commands,
    player_query: Query<(Entity, &PlayerId), With<PlayerMarker>>,
    weapon_query: Query<(&Size, &Transform), With<WeaponMarker>>,
){
        for (entity, player_id) in player_query.iter(){
            for input in input_reader.read(){
                if let Some(input) = input.input(){
                    match input {
                        Inputs::Build(building) => {
                            if building.building {
                                info!("B is  by client");
                                for event in message_reader.read(){
                                    info!("message is binnen");
                                    let client_id = event.context();
                                    if &PlayerId(*client_id) == player_id {
                                        let replication = Replicate {
                                            prediction_target: NetworkTarget::Single(*client_id),
                                            interpolation_target: NetworkTarget::AllExceptSingle(*client_id),
                                            // replication_mode: ReplicationMode::Room,
                                        ..default()
                                    };
                                    let transform = event.message().player_transform;
                                    let duration = 3.0;
                                    let health = 100;
                                  info!("weapon build");
 //spawn entity                commands.entity(entity).add_child(child);
                                            commands.entity(entity).insert(WeaponBuildMarker);
                                        }}}}
                        _ => {}}}}} 
}```
#

the first info!() with b is, always triggers when i press b, but the info!() after that doesn't always trigger and that would mean that there is no message but i send the message when i press b

pine cape
#

You want to spawn a weapon on the client when a button is pressed, but the server should be aware of the weapon's transform?

bronze elm
# west garnet ```pub(crate) fn spawn_weapon_bundle( mut input_reader: EventReader<InputEve...

I think messages might be the easiest route as Periwink mentioned. Whenever you run into the case of “request-response” it’s easiest to just send a message, because you could trigger the message client side from the input, send the message containing the transform at the time it happened. And then server side optionally validate / sanity check that transform (maybe some distance check and what not) and then have the server insert a component which will replicate it back to the client

west garnet
#

oh could it work if i discard the whole input part and just place the weapon when the message comes in?

#

and then just send the message when i press the b key

pine cape
#

In this case it might make sense to do pre-spawning like in here:

  • you run a system on both client and server to spawn a weapon when an input is pressed
  • it will run first on the client timeline, since the client runs a bit in the future (predicted)
  • then it will run on the server to spawn the weapon there as well
  • then the server will get replication authority over the client entity
GitHub

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

#

Otherwise, yes just what panjeet said: listen for input event on client. When an input comes, spawn the weapon on the client, and send a message to the server with the transform of the weapon

#

But it would be easier if you described the high-level thing that you're trying to achieve; is it spawning a weapon on the client? Would the server have authority over the weapon?

west garnet
#

tnx i think i can work with this, i'll take a look at pre-spawning

bronze elm
# pine cape Otherwise, yes just what panjeet said: listen for input event on client. When an...

On a related note, when I handle client side inputs I typically use the bevy inputs directly, since I haven’t really dove into leafwing. Is there a way to access the leafwing inputs client side that won’t interfere with lightyear? Or are you not consuming the events? Cuz one thing that confuses me ergonomically is that if you defined an action that is only used client side I guess that would still be synced

#

Or can you make a separate client side action state with a separate inputmanagerplugin that is not included in the protocol

pine cape
#

Yes you're free to use the leafwing input manager plugin directly for inputs that you don't want to be sent to the server

bronze elm
#

Update on rust in rust... added modular + rigged characters, syncing the other players' camera forward (just a component added to the player bundle) and rotating the head bone. Also added a terrain blending shader

stiff quiver
#

@pine cape since we seem to have gathered every option we have on the throttling issue, have you decided on a path moving forward?

pine cape
#

I'll try to do a prototype where the client sends a message to the server to 'pause' the connection when it detects that the tab is about to get suspended. When it comes back to the tab, it sends a message to 'resume' the connection

stiff quiver
peak nymph
stiff quiver
#

@pine cape what exactly is the xpbd feature for? :v

#

doesnt xpbd already have impls for all necessary traits used for replication

pine cape
#

It inplements linear interpolation traits for the xpbd traits

stiff quiver
pine cape
#

No, the types need to implement the Linear trait

stiff quiver
#

hm alr

bronze elm
#

If an entity is despawned, what is the expected behavior with children? E.g. I have a player entity on which I insert a bunch of children like meshes on the Interpolated version. But when the player despawns those child meshes are still visible on screen. What's confusing is they don't show in the inspector anymore. Is lightyear supposed to despawn recursive on that entity for you, or is the idea to handle it during the EntityDespawnEvent manually?

pine cape
#

I call despawn_recursive on the Confirmed entity when the remote entity is despawned, but I only call despawn on the Interpolated entity; I can change it to despawn_recursive

pine cape
#

Added

bronze elm
bronze elm
#

I think there might be a bug with the PartialEq change. Sometimes when an entity is spawned, there is a possibility that the Interpolated entity does not get updated. As you can see here Position is set to all 0's on the Interpolated entity while Confirmed has values.

Component setup:

        app.register_component::<Position>(ChannelDirection::ServerToClient);
        app.add_prediction::<Position>(ComponentSyncMode::Full);
        app.add_interpolation::<Position>(ComponentSyncMode::Full);
        app.add_interpolation_fn::<Position>(PositionLinearInterpolation::lerp);

Replication config on the entity:

       Replicate {
            target: ReplicationTarget {
                target: NetworkTarget::All,
            },
            sync: SyncTarget {
                interpolation: NetworkTarget::All,
                ..default()
            },
            ..default()
        },

It's very unpredictable, sometimes when it spawns it updates but other times not. E.g. I can have one player see the right Position and another is stuck at 0, 0, 0

#

If an update does come through from the server it will update, but that initial setting of the value is acting strange

pine cape
#

We don't add the Interpolated component immediately on the interpolated entity

#

instead we wait for either:

  • we have received 2 server updates
  • at least 1.3 s * send_interval has passed
bronze elm
#

Hmm ok, in this case the entity is not being actively controlled, so when it stops moving the xpbd is putting it to sleep so there are no updates happening on it, so by the time a player sees it there are no changes

pine cape
#

Do you see this problem only when the entity gets spawned?
Or also afterwards?

bronze elm
#

So far only at spawn. Like it will spawn at 0, 0, 0 on the screen, if I nudge it that triggers an update since the physics wakes up and then I will get the Position to update on the interpolated entity

#

I guess this makes sense, if ComponentSyncMode::Full is defined as " Interpolated: we will run interpolation between the last 2 confirmed states"

But if I have a Confirmed position client side on spawn, I would expect that to maybe be the initial value or something

pine cape
#

Ok thanks, that shouldn't happen. Normally the logic is:

  • spawn Interpolated entity
  • if there's 2 server updates, insert the component with interpolation
  • if we don't have 2 server updates (because the character was spawned without moving) but 1.3 * send_interval has elapsed, we insert the component with the initial server value received. (to avoid the problem of not having 2 server updates if the character didn't move at all after spawn)
#

I guess what I don't get is why it spawns at 0,0,0 on the screen. Normally the component is not even added initially on the Interpolated entity

#

Maybe you're adding the Transform yourself by adding a mesh? Initially the Transform or Position is not inserted on the Interpolated entity

bronze elm
#

I don't replicate Transform, only Position

#

I grabbed your debug_interpolate from one of the examples if this helps:

2024-05-19T02:06:08.772254Z  INFO app::world: tail_status=InterpolateStatus { start: None, end: None, current_tick: Tick(1253), current_overstep: 0.28259802 } tail_history=ConfirmedHistory { buffer: ReadyBuffer { heap: [] } }
2024-05-19T02:06:08.790999Z  INFO app::world: interpolation debug tick=Tick(1261)
2024-05-19T02:06:08.791383Z  INFO app::world: tail_status=InterpolateStatus { start: None, end: None, current_tick: Tick(1254), current_overstep: 0.3851324 } tail_history=ConfirmedHistory { buffer: ReadyBuffer { heap: [] } }
2024-05-19T02:06:08.805182Z  INFO app::world: interpolation debug tick=Tick(1262)
pine cape
#

But are you adding Position yourself on the PredictedEntity in any way?
(Maybe by adding Transform, and then xpbd inserts a Position component?)

Because normally Position would get inserted here: https://github.com/cBournhonesque/lightyear/blob/71308affc88b25d53e4bb1d4d4514a3306890395/lightyear/src/client/interpolation/interpolate.rs#L246-L246
but this system does not run if Position already exists on the entity

GitHub

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

bronze elm
#

Oh, actually I am lol

#

let me take that out and test

#

Yeah, I think that was it... I had Position::default(), in there... which makes sense why it was 0, 0,0 as well

pine cape
#

Yes the current system is a bit confusing

#

The wait for 2 server updates didn't work well for moving objects like bullets because the bullet would be frozen in place until we had 2 server updates

#

But for things like player characters it's ok to just wait for 2 server updates

jade ember
#

Is there a way to figure out if I'm the server?

I'm using the host-server mode, and i'd like to have some systems only running if i'm the server/host

bronze elm
pine cape
dull lion
#

I decided to create an IsServer resource on the server side and then use that with resource_exists

#

but a convenience like this could maybe be upstreamed?

#

also my steamworks account came through so i'll be digging into that again when i have time

pine cape
dull lion
#

yes

pine cape
stiff quiver
#

@pine cape according to my tests this should work

#

so maybe making a custom scheduler plugin could work

#

(i might not be up to date on the state of this tho, so maybe u already solved that issue haha)

pine cape
#

do you want to collaborate on that branch?

#

The idea would be to send a message from client to server when the tab gets hidden

#

I think the rest would work

stiff quiver
stiff quiver
#

yea that wont work

pine cape
#

how come

stiff quiver
#

it only fires on doc load and unload

#

not just when the tab is hidden

pine cape
#

Ok i'll try visibilitychange

stiff quiver
#

this should be the way to go i think

pine cape
#

the thing is that i don't know how to have access to my bevy resources (ConnectionManager) from within the callback

stiff quiver
#

u can also check for document.hidden btw

#

as a way to do that in sync

#

maybe in the callback

stiff quiver
#

iirc it takes an rc of the app and passes it to the runner closure

#

so maybe we can somehow reuse that rc

pine cape
#

hm maybe

#

I tried to listen to the winit event WindowOccluded, but since bevy is paused I only get access to it when I come back to the tab

stiff quiver
#

hmm

#

yea

#

so maybe just a custom schedule runner

#

idt anything else makes sense

#

because the runner would consume the rc<app> i just saw

#

so we cant get it anymore

#

we could do some black magic by illegally cloning the ref to the app since we are in wasm land and everything is single threaded

#

but im not a big fan of that tbh

pine cape
#

Not a fan of a custom schedule runner either; it would be similar to the winit one, except that we run one extra app.update() when the tab gets hidden?

stiff quiver
#

except bevy doesnt support that rn, so we would have to do some tricks to get a ref of the app

pine cape
#

maybe the best way is to add this to bevy if other people need it

stiff quiver
#

i wonder if the other networking libs solved that issue

#

lol whoever made this really didnt want anyone to understand this

pine cape
#

where is this from

stiff quiver
#

schedule runner plugin

#

this is how a wasm app would be run without winit

pine cape
#

wouldn't we want a modified version of the winit plugin?

stiff quiver
#

idk what happens when u add winit and scheduler to the same app

#

hm

stiff quiver
pine cape
#

so the ScheduleRunnerPlugin ticks every tick_duration even if the app is backgrounded?

stiff quiver
#

general rust principles tell me its not possible

pine cape
#

(apart from throttling)

stiff quiver
#

but idk

stiff quiver
#

but the setTimeout itself would be throttled on the js side

#

so it doesnt really matter

pine cape
#

the problem is this doesn't use requestAnimationFrame anymore

stiff quiver
stiff quiver
#

the question is, can this work alongside winit

#

or does this replace winits event loop

#

apparently it does

#

welp

#

ok new plan:

  1. figure out how to extract the runner fn from winit (im thinking since its setting the runner fn we can just intercept it after adding the plugin?)
  2. modify the scheduler plugin to call winits event loop if existing (if not, just app.update()) on every rAF / settimeout / once, whatever the user needs
  3. since we have access to the rc<app>, make another closure that runs on pagevisibility
#

@pine cape sounds good?

pine cape
#
  1. in lightyear's ClientPlugin, we can just call app.set_runner() to override the runner
  2. create our own version of winit_runner, which just calls app.update() once on pagehidden (or whatever the even is). Or just use rc<app> with a closure that sends the message to the server
stiff quiver
pine cape
#

i think we can just say that apps have to use winit to run in wasm with lightyear

stiff quiver
#

sounds like way too much work

pine cape
#

99% sure already do, no? since it's in the DefaultPlugins

#

by making our own I just mean copy-paste the code

stiff quiver
#

thats all the bevy stuff im using eg

pine cape
#

doesn't winit provide the input events?

stiff quiver
#

yea, i just add them myself lol

#

its a bit messy but allows u to do ur own scheduling

#

well shrug

#

ig having winit as a req makes sense too

pine cape
#

I mean some users use ScheduleRunnerPlugin and some use WinitPlugin

#

whatever is easier for you

stiff quiver
#

thats what im doing at least

#

but its possible to just Winit + no renderer

#

so

#

shrug

stiff quiver
#

basically what i mentioned earlier but they unsafely duplicate the world ref, not the app itself

stiff quiver
#

seems to work, test gets logged but the entity doesnt actually seem to get spawned

#

yea that doesnt seem right

stiff quiver
#

i cant believe this works lmao

#

@pine cape well i guess thats one way to make callbacks which run the main schedule

#

like this actually works without errors or smth

#

its technically unsafe due to concurrency issues but since we are in wasm land its not

#

ig u could prob get rid of the static mut

#

but im not that good at unsafe rust so i just gone the easy way

pine cape
#

this isn't bad! maybe we can integrate this with winit (or with the scheduleRunnerPlugin)

stiff quiver
stiff quiver
#

ah nvm

#

same thing with plugins

#

so ig only option are systems

#

prob has to do with the mem::replace in app::run

#

@pine cape only 1 system

#

toggle could just be some lightyear resource ig

#

but yea i tested and this actually runs all systems etc

#

unless u replace the main label during app creation

#

looks good or u got any improvements?

stiff quiver
#

@pine cape after some tests i can conclude that at least on chromium based browsers we can run the main schedule consistently at an interval of one second when the tab is inactive

#

is that good enough?

#

i havent tested on firefox yet though

#

and no way for me to test on safari either

#

firefox is 1 sec too

#

relatively clean implementation with visibility change listener + set interval

#

im guessing you could also just have one setinterval that checks for document.visibilityState and if hidden runs the schedule

pine cape
#

do you have a branch I could look at? how do we restore the original schedule when the tab becomes unhidden?

stiff quiver
stiff quiver
#

that way we dont actually timeout

pine cape
#

but why doesn't this callback also get throttled?

stiff quiver
wintry dome
#

i was reading the book section on prediction and rollback about how you get a Confirmed and a Predicted entity on the client. wondering how/if that can play nice with physics (like xpbd), since if you have a second confirmed entity with various physics components, it will interfere with the simulation. is it sufficient to ensure the Confirmed entity doesn't get a collider or rigidbody?

dull lion
#

I make liberal use of Or<(With<Interpolated>, With<Predicted>)>)> to add things like physics components. I haven't felt the need to use the Confirmed entity at all so far

wintry dome
dull lion
#

my static level colliders are not predicted, and then i just skip that whole Or<...> incantation. apart from that there isn't a whole lot moving around at the moment, except player movement and bullets which are both predicted

wintry dome
#

ok, thanks

stiff quiver
wintry dome
#

i'd need to predict other physics objects, so collisions between players and physics objects aren't janky

stiff quiver
#

well i guess bullets too

wintry dome
#

yeah doesn't matter about static stuff

pine cape
#

Yes the Confirmed entity is just there to receive replication updates, and then sync them to the Predicted or Interpolated entity. In practice you would either:

  • add physic components on the Predicted entity if you run prediction
  • don't add anything and use interpolation
wintry dome
#

so i should be able to add physics comps to all the Predicted stuff just fine, players and non-player physics entities

#

for "everything is predicted, with rollback" mode

pine cape
wintry dome
#

thanks, will check it out

#

any known issues/bugs with rollback related stuff atm?

pine cape
pine cape
# wintry dome any known issues/bugs with rollback related stuff atm?

Hm well the leafwing_input demos is not that smooth when predicting other players. I don't know if that's just because the client doesn't have access to other players' inputs or because of some other bug. I added Correction to smooth out the rollback over a few frames instead of rolling back instantly, but I'm not 100% sure that is working

stiff quiver
#

@pine cape btw should i make a branch for fixing the tab switching stuff or do u want to solve it in another way?

pine cape
wintry dome
#

are you sending player 1's inputs out to player 2 immediately when they are received by the server, even before applying them? because with reasonable latency and a few ticks of input delay, it's quite likely that you could get the inputs before they're needed

#

that reduces the number of frames of inputs you have to guess/extrapolate

pine cape
wintry dome
#

yeah i'm seeing some jank for the remote player movement with the leafwing demo – which technically could be buttery smooth if the remote player is just holding down the same direction key right - since you just assume the missing inputs are the same as the last known input for remote players. (jank is when you mispredict because they change direction)

pine cape
#

yea, if the remote player is holding down their input key I would expect it to be completely smooth

#

which is the case if you set Correction to 0. (correction_ticks_factor= 0.0)

pine cape
wintry dome
#

yeah i think the server needs to be prepared to send packets every single tick as needed really

#

for inputs, at least

pine cape
#

I have to think about how to do that.
Maybe the best solution is that by default the server can send packets every single tick, and then you can configure the send_interval for every channel or entity. This would allow me to solve https://github.com/cBournhonesque/lightyear/issues/126 as well

wintry dome
#

gaffer talks about a prioroty accumulator for that.. i would be happy with inputs every tick, and player updates much more frequently than random physics objects

#

(rocket league sends the ball updates every tick but players less often iirc)

#

hmm even with correction=0 still some unexpected jank on the leafwing demo. i just edit leafwing_inputs/assets/settings.ron right?

pine cape
#

I already have the priority accumulator implemented, but that only kicks in if you limit the bandwidth; but Unreal does both priority accumulation + setting a replication_rate per interval. (unreal flow)
but yeah let me get back to it.
We could:

  • just use priority accumulation with a bandwidth (by default input priority is basically infinite)
  • remove send_interval and make send_interval configurable per entity/channel
  • have send_interval + a custom_send_interval configurable per entity/channel
pine cape
#

Is it some very tiny jitter? that might be because the position is updated over FixedUpdate, so you need to visually interpolate during PostUpdate (book)

stiff quiver
pine cape
stiff quiver
wintry dome
#

my hot take on that: entities could have a minimum time since last send before they start automatically accumulating priority (obv things like collision might just set priority to 1.0 immediately). a cooldown period of sorts. boring objects have a longer cooldown set. then each tick, the server should send anything with a priority > 0 that fits in the bw budget. i'm sure there are various ways to do this though.

stiff quiver
#

plus it seems as if webworkers dont get throttled as much so maybe we can even get below 1s

wintry dome
#

i think it's reasonable for the server to send a packet every tick, just containing all the player inputs tbh, even if there's nothing else worth sending.

stiff quiver
#

@pine cape should we have a delay (like rAF running at the monitors framerate eg) while the window is inactive?

#

im thinking it might be useful to have this user configurable

#

but idrk

#

or maybe imitate raf framerate

#

but that would be hard without collecting stats about frame time i think

#

also should we actually let lightyear know its in background mode or just business as usual

#

cuz it seems like a waste to continue sending inputs while in bg

pine cape
#

well we'll probably go with option 1 in the end, but it would be nice to see if option 2 works first

stiff quiver
#

webworkers have no throttling at all

#

even after 5+ mins

#

idk for mobile but at least in desktop it works with chromium and firefox

pine cape
#

you didn't spawn a webworker though, no?

stiff quiver
#

im just thinking that we should prob have some kind of delay

stiff quiver
pine cape
#

what about the option without? doesn't webworker kill performance?

stiff quiver
#

not really

#

there is only like 16 ms delay when switching from active to bg cuz of the latency

pine cape
#

I thought we were trying without webworker, with 1 second delay; and check if the keepalives are still sent

stiff quiver
pine cape
#

I think it would be good to document all this, could you open an issue or a PR with what you tried?

stiff quiver
#

yea i made a branch rn

pine cape
#

so we can know for exampel that the delay becomes 1 minute without web worker

stiff quiver
#

ill prob link the pr with the existing issue

#

ok apparently there is no throttling for webworkers on mobile either (at least android / chrome)

#

i mean it kinda makes sense that thats the case ig

pine cape
#

just in the PR description is fine i think

stiff quiver
#

ok

#

my branch btw

pine cape
#

so with webworkers, you would have a resource to toggle?

stiff quiver
#

no

#

we just check for document.hidden every message

#

if hidden we run the app.update

#

its a bit wasteful but still better than making a new worker everytime the tab is in background

#

plus we can still optimize this a lot by just having a delay in the workers setInterval

pine cape
stiff quiver
#

yea im testing locally

#

idrk yet where to put this

#

i mean its just a startup system so maybe in utils

#

¯_(ツ)_/¯

pine cape
#

or just add your stuff in simple_box

#

I'd like to see it work in a real example

stiff quiver
#

oh yea

#

kk

stiff quiver
pine cape
#

yes

stiff quiver
#

@pine cape i think should work now

pine cape
#

wow it works really well, amazing

pine cape
#

this might even be better than 'pausing' the connection because there's no need to figure out how update the connection timeouts on the fly. + those update calls don't run rendering so they should be fairly cheap; they just keep the local ECS up-to-date with the last 1 second of buffered data

#

I might want to add a new NetworkingState Background or Paused, where some plugins stop running (Inputs, etc.)

#

but your webworker runs at 60Hz?

#

dont' we want 1 Hz?

#

also is calling clear_trackers() needed? isn't that part of the Last schedule?

pine cape
#

Maybe you could create a separate crate for this and post in #web or #crates ? I think a lot of people would be interested in this functionality

pine finch
#

Hi there! I'm trying to cook up a stupid-simple config for experimenting (no replication, just a Message and Channel) and and I'm running into a never-ending string of lightyear::connection::netcode::client: error updating netcode client: Transport(Io(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })) on the client. Since you can't add/remove plugins at will I'm starting up the client with the ServerPlugins, might that be causing it?

pine cape
#

Hm i don't think i have enough information to help you; do you have some code you could share? You can DM me

pine finch
#

Fixed (❤️ periwink), but for future reference;

  1. client::NetConfig::NetCode needs a Manual configuration specifying the remote IP
  2. client::IoConfig doesn't refer to the remote host address, keep it on localhost port 0 like the docs tell you to
pine cape
stiff quiver
stiff quiver
stiff quiver
stiff quiver
# pine cape dont' we want 1 Hz?

ah on a side note, i think i mentioned before, but this is unreliable anyways (timers are much more unreliable than rAF), so ideally we would want a bit faster, maybe 2-3 updates per second or so for redundancy

#

but yea should be handed off to the user ig

pine cape
wintry dome
#

i set correction_ticks_factor: 0.0 and conditioner: None for the leafwing demo. added debugging for when rollbacks happen. observed that while player 2 holds down (say) up, player 1 continuously does rollbacks. i would expect there to be a single rollback when player 2 depresses up, after which player 1 just predicts – correctly – that up remains depressed, so no more rollbacks necessary until player 2 releases the up key

#

ie, i expected a single rollback per unpredictable event, such as a remote player keyboard state changing.

wintry dome
#

digging in further to the leafwing example - i was expecting to see a history for inputs (PlayerActions) at each tick, for the player cubes, so that during rollback you can use the inputs for the currently-being-resimulated tick in the player_movement system (same for local and remote players). am i right in thinking this isn't happening?

wintry dome
#

ah that's in InputBuffer within the input_leafwing plugin it seems

wintry dome
#

however, the remote player appears as two entities, one with Confirmed, which has the replicated ActionState but not collider/rigidbody stuff, and one with Predicted, which doesn't have ActionState, but has all the physics components (ie, the one we render). so the player_movement system can't be moving the Predicted remote player, since it doesn't have ActionState? doesn't it need to be running movement based off historical inputs during rollbacks for resimulation to work properly, is that happening somehow?

wintry dome
#

adding this system simulates the remote players properly i think - i just get one rollback per key change now:

// take actionstate from remote confirmed players, use it to move the predicted player
#[allow(clippy::type_complexity)]
fn remote_player_movement(
    tick_manager: Res<TickManager>,
    confirmed_players: Query<(&Confirmed, &ActionState<PlayerActions>)>,
    mut predicted_players: Query<
        (Entity, &mut LinearVelocity),
        (Without<ActionState<PlayerActions>>, With<Predicted>),
    >,
) {
    for (confirmed, action_state) in confirmed_players.iter() {
        if let Ok((e, velocity)) = predicted_players.get_mut(confirmed.predicted.unwrap()) {
            if !action_state.get_pressed().is_empty() {
                info!(
                    "Applying movement to remote player {e:?}: {:?} @ {:?}",
                    action_state.get_pressed(),
                    tick_manager.tick()
                );
                shared_movement_behaviour(velocity, action_state);
            }
        }
    }
}
pine cape
#

Thanks, this sounds like a bug introduced by the protocol refactor; I think the Predicted entities for the remote players should have ActionState (and maybe History<ActionState>?)

#

I pushed a fix: https://github.com/cBournhonesque/lightyear/pull/374
Thanks so much for looking into this, I definitely need more tests around multi-player prediction. I think I'm going to handle this differently though; as you said I don't wait until the ActionState component is replicated, instead I might want to forward the LeafwingInputMessage immediately as you said (which contains inputs a bit ahead of time compare to ActionState, since ActionState waits until the inputs are applied

GitHub

Prediction for other players was broken because we weren't copying the ActionState component to other players Predicted entity, like we used to: https://github.com/cBournhonesque/lightyear/blob...

wintry dome
#

cool. yes that did the trick. removed my system and still just seeing 1 rollback per input change now

#

but yeah agreed, need to send out inputs from the server immediately, which should reduce mispredictions in various setups

pine cape
#

One issue is that the LeafwingInputMessage only contains diffs as an optimization, but for other players we wouldn't have a base state to start with. Maybe I can replicate other players ActionState once every few seconds, and apart from that rely on the input message.

pine cape
# stiff quiver Hell yea, awesome :)

also what's nice about your solution is that it doesn't seem to call any render system?
Since app.update() is not called, we don't run the rendering sub_app

stiff quiver
#

Ah but if users have sub apps that kinda sucks no?

pine cape
#

i think that's fine; users don't have subapps 🙂

stiff quiver
#

why not

#

Cant they make subapps too

pine cape
#

yeah but no one does, we can worry about that when someone runs into an issue

stiff quiver
#

hm alright

#

nice side effect then i guess

pine cape
# wintry dome but yeah agreed, need to send out inputs from the server immediately, which shou...

Actually I'm not sure if that's even possible.
One of my invariants is that all the components for a replicated entity are on the same tick, the "confirmed" tick.
I do that so that when I apply rollback I know that the entity state is exactly what was on the server on the confirmed tick, and the rollback can work properly.

But with this change we might get input updates (i.e. ActionState updates) more frequently, so the tick of the ActionState might be different from the "confirmed" tick of the replicated entity.

Or is the goal not to have more frequent rollbacks based on inputs; but to have access to more of the remote players' inputs when we do rollbacks?

wintry dome
#

it makes sense that components for an entity are all on the same tick. but having the remote players' inputs as soon as possible means you can can use them during simulating frames that are ahead of the server, which will include various rollback scenarios. it will probably eliminate some rollbacks altogether, or at least reduce their size. since a big source of mispredictions will be when remote users change which keys they are pressing.

#

somewhere i have code like this:

// per fixedupdate frame not during rollback:
let inputs = get_keys_pressed();
let tick = tick_manager.tick();
store_inputs_for_tick(tick + input_delay_ticks, inputs);
#

and then more code which gets the stored inputs for the tick_manager.tick() and writes to the player's InputsThisFrame component (or something like that)

#

not sure how that fits with the current idea of replicating all components. maybe the buffer of unapplied inputs could be a component which is replicated every tick, and a system that applies from the buffer as needed

#

havin access to more of the remote players' inputs when we do rollbacks leads to fewer rollbacks overall

pine cape
#

makes sense, i'll try to implement that today

peak nymph
#

Should Confirmed entities be automatically despawned if their Interpreted or Predicted counterparts get despawned or is that something we need to handle?

#

Seem to have some sticking around, but haven't dug in too deep yet

pine cape
#

I don't think so, I think the Confirmed entity is only despawned when the entity is despawned on the server. The Confirmed entity is purely there to receive the replication updates

peak nymph
#

oh right that makes sense, let me check if they're getting stuck on the server

peak nymph
#

can't repro it now

#

¯_(ツ)_/¯

wintry dome
# pine cape makes sense, i'll try to implement that today

i suggest to reuse the last known input if no input is available for a remote player at the current tick. ie, assume if they were holding up, they are still holding it. keeping that guesswork simple like that worked best for me. i tried a few other more complicated heuristics but just copying the last input worked best. i'm not sure if that's happening at the mo.

wintry dome
#

even past the last frame received from the server? ie into the future

pine cape
#

yea, i keep using the last ActionState received

wintry dome
#

cool

pine cape
#

I think the current system works well, we just might be losing a few ticks from the server that we might have if we send the Inputs directly instead of the ActionState.

Currently: we rollback at tick 12 because we received a server update that shows a mismatch; we have the ActionState at tick 12. During rollback we use the ActionState from tick 12 (i.e. we consider that the remote player keeps pressing a key)

Planned: we rollback at tick 12 because we received a server update that shows a mismatch; we have received an InputMessage that contain InputDiffs from the remote player up to tick 14, which allows us to reconstruct the remote player's ActionState up to tick 14. We do the rollback, using the remote player's ActionState from tick 12, 13, 14 (and then we keep the ActionState of tick 14 for the rest of the rollback)

#

The one thing I'm worried about is that the server is sending remote player's InputDiffs instead of the complete ActionState. This probably breaks if the remote player entity gets replicated while the other player aws pressing some keys, because applying the diffs won't give you the correct InputState.
I can just default to sending the full ActionState for now.
I guess this is a bigger problem: how to do delta-compression correctly by sending a base state and then diffs, and how to recover if something goes wrong.

wintry dome
#

sounds good. how important is diffs for the input state? 1 byte is ample per input in the demo, up/down/left/right.

#

could just send the full input for now. altho more complicated games might have larger inputs.

pine cape
#

I'm using leafwing input manager, where the inputs (ActionState) have more data (durations, etc.)
But yes doesnt matter too much for now

wintry dome
#

oh yes true, much more than a bit per key

#

if you have to send 4 inputs, ticks 12,13,14,15, maybe send full for 12 then 3 diffs

bronze elm
#

does channel prioritization happen on a per-connection basis or is it across the board? like if you use a channel for game messages and had another separate one for like voice chat or something, and you always want game messages to be higher priority, if like 100 people all used voice chat at the same time, would that bottleneck the game messages?

pine cape
#

The bandwidth cap and the prioritization are both per connection!

pine cape
#

@wintry dome I merged the change for remote player prediction: https://github.com/cBournhonesque/lightyear/pull/376
I wasn't able to have a fully clean implementation but it does seem to help. I've checked in the logs that we are using the raw remote player input messages during rollback to have more precision during rollback.

GitHub

Fixes #369
This is not a perfect solution, but the idea is:

we still replicate the ActionState directly. That means that if a rollback happens for a remote player's entity, we will have the ex...

wintry dome
stiff quiver
#

@pine cape idk if thats already the case (didnt seem so) but wouldnt it be more reasonable to only replicate player inputs to the client for players that are actually replicated to the client?

#

maybe i missed it. But it seemed like u are sending inputs from every single connected player

pine cape
pine cape
pine cape
#

this is awesome!

wintry dome
#

how about something like this, writing to a metrics resource, then flushing to diagnostics periodically

#

i wrote something similar for replicon too

#

i extended the existing client diagnostics plugin. i could imagine adding lots more diagnostics, since there's plenty you'd want to display during dev/debugging. separate requirements from exporting to prometheus

pine cape
#

Yes prometheus/grafana doesn't have any in-game overlay, which you need for debugging

#

is it ok if i make a new branch from your changes? I want to change some names, etc.

#

Actually i'll merge your change first

wintry dome
#

yes go ahead

dull lion
#

nice stuff! i would love to be able to have graphs of bandwidth consumption over time, and a list of what's taking up that bandwidth. maybe i'll get to that at some point

pine cape
#

There must a plugin somewhere that computes graphs over time from diagnostics data

dull lion
#

(and it would have customizable little plotting displays)

dull lion
pine cape
#

Or is the problem that if we just send a rollback measurement to diagnostics, we wouldn't be able to get the total count? since the total would just be computed over the history?

wintry dome
#

yeah you're at he mercy of diagnostics, can't easily calculate eg rollbacks per second if you write directly I think

#

hence the resource and system to do any custom things before writing diags

#

and access to history too, for graphs. that could be in the metrics resource in future

#

I treat diags like an executive summary 🤷‍♂️

pine cape
#

Another problem is that diagnostics doesn't have good support for 'namespaces'
ideally i would want one diagnostics per connection on the server

wintry dome
#

yes, true. I think a custom metrics resource will be increasingly useful

bronze elm
#

https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/connection/netcode/server.rs#L590

I know this wouldn't be following the Netcode standard. But do you think it would be worth adding a status flag to DeniedPacket and sending back the specific reason? Right now, the client only ever just gets a generic connection error. But it seems strange to have "denied" send only when it's full. One example is if you forget to change your client id, or if the same client id is logged in. The context only really exists in these debug statements. Also a related "nice to have" would be some way to make pluggable validations like to be able to add your own condition of whether a client should be denied for bans or whatever

GitHub

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

pine cape
#

both suggestions make sense to me! creating tickets for them

stiff quiver
#

@pine cape #crates message

#

:p

stiff quiver
#

less bandwidth waste rejecting invalid cons

bronze elm
# stiff quiver less bandwidth waste rejecting invalid cons

I feel like the value you get from a UX standpoint is a better tradeoff than worrying about sending 1 byte. I could see maybe with raw UDP but pretty sure webtransport or anything else really, is either going to mitigate that (e.g. in the web transport spec data sent by client before the server permits it is invalid behavior) or make it worse (as in the ddos bottleneck will happen in the transport layer, like flooding a websocket which has head of line blocking)

stiff quiver
jade ember
#

@dull lion is there already a way for me to access the steam client as a resource?

dull lion
#

on my branch, or on lightyear main?

jade ember
#

on your branch :)

dull lion
#

on my branch you can create the steam client yourself and stick it in a resource

jade ember
#

i see

pine cape
jade ember
#

@pine cape is there a way for me to check if the ClientId i get is myself?

basically, i'm looping through all connected players and if it's myself i'd like to do something specific

#

i thought maybe is_local would do that, but that only checks if the client id is of type Local

jade ember
#

my current solution is to add a resource on a on_connect event and store the given ID. then i just compare the two

#

this works, just wondering if there's any other solution

pine cape
#

For host-server mode?

jade ember
pine cape
#

maybe connection: Res<ClientConnection> and connection.id()

jade ember
pine finch
#

What should I consider when using an ActionStateDriver to drive an action? I've got a LeafwingInputPlugin with a DualAxis action that's being set like so from a system:

            action_data.axis_pair = Some(DualAxisData::from_xy(dir));
            action_state.set_changed();
            action_state.press(&PlayerActions::Look);

Server->client replication works just fine, but not the other way round. Can supply more code if needed

#

If I insert eg. VirtualDPad::arrowkeys into the initial InputMap then it works fine using the arrow keys both-ways

pine cape
#

You need to add an InputMap on your entity for the inputs to be sent from client to server

pine finch
#
        info!("Spawning {:?}", event.client_id());

        let map = InputMap::default().insert_multiple([
            (PlayerActions::Up,     KeyCode::KeyW),
            (PlayerActions::Down,   KeyCode::KeyS),
            (PlayerActions::Left,   KeyCode::KeyA),
            (PlayerActions::Right,  KeyCode::KeyD),
        ]).insert(PlayerActions::Look, VirtualDPad::arrow_keys()).to_owned();

        let player = commands.spawn(
            PlayerBundle::new(
                event.client_id(),
                InputManagerBundle::<PlayerActions>::with_map(map)
        )).id();

        commands.entity(window.single()).insert(ActionStateDriver {
            action: PlayerActions::Look,
            targets: player.into()
        });

Now that I think about it i haven't tried both using the driver and the VirtualDPad::arrow_keys at the same time so ill give that a go

#

If it's an issue with the Look action not being in the inputmap then I wonder if there's a placeholder that allows me to control it via a driver without say arrow_keys()

pine cape
#

I just require an InputMap to track which entity is 'controlled' by the player and add some extra internal input-handling components. But beyond that the only thing that matters is changes to the ActionState, so you're free to update the ActionState however you want (via inputs, ActionDriver, updating ActionState manually, etc.)

pine finch
#

How do you go about messing with the action data? Been messing around with how I update/insert it still isn't replicating client->server

        if let Some(data) = action_data {
            data.axis_pair = Some(DualAxisData::from_xy(dir));
            action_state.press(&PlayerActions::Look);
        }
        else {
            action_state.set_action_data(PlayerActions::Look, 
                ActionData {
                    state: ButtonState::Pressed,
                    axis_pair: Some(DualAxisData::from_xy(dir)),
                    ..Default::default()
                }
            );
        }
pine cape
#

This is a client and a dedicated server, right?

pine finch
#

I'm running a HostServer if that's what you mean

pine cape
#

and the 'client' you were mentioning is the 'host-client'?

pine finch
#

The instance running the server + local client would replicate fine to the just netcode client, but not the other way round

pine cape
#

In which schedule are you updating the ActionState?

pine finch
#

Before I followed this using action_state.action_data_mut and that wouldn't replicate the Look actions presence at all, but after changing to the above the Look action data would show, but none of the changes would replicate to the client. I've tried both what's shown here in the InputManagerSystem::ManualControl as well as in the FIxedSet

GitHub

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

pine cape
#

Maybe also confirm via the inspector that the ActionDiffBuffer and InputBuffer components are added?

#

Or do you have a github I could look at? I would be easier if I could experiment. I've only tried ActionState updated via InputMap

pine finch
#

Ill set that up and dm, one min

pine cape
#

Fixed: the solution was that the ActionState has to be updated in

  • PreUpdate Schedule in the ManualControl systemset set

(we generate the input diffs in PreUpdate after all the leafwing system sets have run)

jade ember
#

@dull lion so i just had my first attempt with your p2p branch, and it failed to connect.
the host received a connection request, but then nothing happened.

I'll look more into it tomorrow (maybe today already) and will look into what the problem is

dull lion
#

are you testing with two steam accounts? i realized you need two machines and two steam accounts to properly test it and then i got lazy 😅

jade ember
#

yep, two machines two different accounts :)

#

i already once implemented steam p2p into my own project, but it got super annoying the need to have a second machine all the time, which is why i decided to switch to lightyear

dull lion
#

right. yeah it's convenient to have the option to not use steam networking during development

#

anyway, looking forward to hearing about your progress! i'll have to get this sorted in the next month or so, but i've gotten sidetracked on some other stuff now

jade ember
#

yea no worries! I currently got some time, as i'm done with most of my final stuff

#

if i find a fix can i just create a merge request on your branch then?

dull lion
#

please do!

jade ember
#

great

wintry dome
#

if i wanted to change the xpbd demo so the player cube is spawned by the server, is it correct for the server to spawn it with SyncTarget{ prediction: NetworkTarget::All }, and an ActionState::<PlayerActions> then when the client notices it (and playerid matches our own), add the InputMap to the predicted entity?

pine cape
#

Yes I think that works; you can also use ControlledBy on the server-side to indicate which clients are controlling the entity. Then the entity on the client will have a Controlled component you can filter with (instead of manually comparing playerids)

wintry dome
#

thanks, will give that a try

#

ah yes i'm already adding ControlledBy, since it's in the Replicate bundle. i should check for that on the client

pine cape
#

yes I should update the examples to use Controlled on the receiver side

wintry dome
#

ah it's the Confirmed entity on the client that gets the Controlled component though

#

2 queries needed. nice that Controlled gets added automatically though, better than comparing ids

pine cape
#

yes; maybe I should sync Controlled automatically to the Predicted/Interpolated entities, since they're also controlled by the same player