#lightyear

1 messages Β· Page 6 of 1

idle pier
#

oh that isn't a real number

#

@pine cape

pine cape
#

Can you try using lightyear main?

#

One bug I fixed recently was that during rollbacks we were still buffering input messages to send

idle pier
#

how recently?

pine cape
#

which means that every rollback was greatly increasing the number of input packets sent

#

2 days ago

#

and you're using leafwing inputs?

idle pier
#

yea LWIM

idle pier
#

i'm one commit behind

#

i'm on df475e9d0d622cf7920dd80b31e705c9bd2c76c5

pine cape
#

df475e9d0d622cf7920dd80b31e705c9bd2c76c5

idle pier
#

yea

pine cape
#

Not sure; can you reproduce this on one of the examples?

idle pier
pine cape
#

I'm not sure, hard to tell without more information. Do you confirm that the extra messages received are InputMessages?

idle pier
idle pier
#

when i disable input plugin, it drops to ~1k packets

#

when i set the send_interval to like 10 sec

#

i still get a bunch of replication updates

pine cape
#

idk i just tested send_interval = 1s in the simple_box demo and it seems to work

idle pier
#

i'm using webtransport

#

that's the only difference i can think of in config

#

when I set the bandwidth limiter, it is properly limited

#

i mean i can't reproduce it on the examples at all trollpainting

#

i think it's very much a me issue

idle pier
#

yea, it was the way i initialised client/server plugins

#

i replaced the clientconfig/serverconfig resources later

#

with a specific address

#

and it doesnt update shared config

pine cape
#

cool

eternal basin
#

Is there a way to log the IP of clients connecting to the server?

I did a bit of digging and I can see where the ClientId -> IP mapping existing, but it's, of course, in the Netcode specifics, which is like four layers below my system that's receiving the Lightyear connection events.
I'm unfamiliar with the other transports, so perhaps the question is meaningless in their contexts, and that's why it's not easy to get at.

pine cape
dull lion
#

If you're connecting via steam there is no visible IP, so it can indeed vary

unkempt sedge
# pine cape I don't think there's a public way to do it, It should probable get added to thi...

// Example of a component that contains an entity. // This component, when replicated, needs to have the inner entity mapped from the Server world // to the client World. // You will need to derive the MapEntities trait for the component, and register // app.add_map_entities<PlayerParent>() in your protocol #[derive(Component, Deserialize, Serialize, Clone, Debug, PartialEq)] pub struct PlayerParent(Entity); In this comment you are basically telling me that if an entity is pointing to another entity, they should always have the map_entities impl. Might I ask why? Is it because server need to find them?

pine cape
#

Let's say you spawn two entities 1 and 2 in the client world, and you have a component PlayerParent(2) added to entity 1.
When you replicate those entities to the server, they will be assigned new entities on the server world (for exampel 36 and 37). If we just replicate the PlayerParent(2) as is, it will point to a different server entity

#

So the MapEntities is just to tell the server to convert PlayerParent(2) into PlayerParent(37) upon receiving the component

unkempt sedge
#

Okay I got it thanks

#

Another question lets say I have an event that sended a vec2 Should I make it into a message like MovePlayer and that moves the player in client?

#

I got a little confused on how I can do this

#

Oh wait saw an similar example here forget about it

pine cape
#

Events that directly move the player or are input related should be handled via Inputs instead

unkempt sedge
pine cape
#

Yes

unkempt sedge
#

What is your goto? I saw in your game you went for the latter

#

And since you are the boss oughta to follow your footsteps honhon

pine cape
#

I'd stick to the simple version for now

unkempt sedge
#

Ah yes I see

unkempt sedge
#

No reaction, oh cmon it was midly amusing

pine cape
#

i was afk :p

#

feel free to try the harder version πŸ™‚

unkempt sedge
#

Why I get the feeling I might regre this

unkempt sedge
#

@pine cape Master look at my magnificient creation

#

took me a while becau i am trying to follow workspace workflow since I already have a full game

#

ah this message is just good

pine cape
#

awesome, glad it works!

idle pier
#

some really intersting PRs open related to FixedUpdate and physics

#

currently building my stuff against it to see if it fixes an issue I have

#

i was also wondering if there's any known issues related to packet loss and the LWIM integration

#

because as soon as I use an OS level conditioner to run the avian example with 0.2 packet loss rate

#

lightyear seems to refuse some input packets

#

and it's causing the FPS to drop due to excessive rollbacks on the client side

pine cape
#

The server is refusing input packets?

#

A lot of rollbacks is not surprising, the client and server state might diverge if you have this much packet loss (although normally with redundancy it should still be ok)

idle pier
wintry dome
#

i'm using this pattern to add VisualInterpolateStatus, checking Without<Confirmed>, but it's still adding the interp stuff to my confirmed entities on the client (which is causing some weirdness i think). Is it possible that replicated components like Position are inserted before Confirmed? and if so, can we consider that a bug?
here's how i'm adding them (basically like the spaceships example). I use Without<Confirmed> instead of With<Predicted> so that if i run the server with a gui, it also gets visual interp.

// ..
app.observe(add_visual_interpolation_components::<Position>);
app.observe(add_visual_interpolation_components::<Rotation>);
// ..
  
fn add_visual_interpolation_components<T: Component>(
    trigger: Trigger<OnAdd, T>,
    q: Query<Entity, (With<T>, Without<Confirmed>)>,
    mut commands: Commands,
) {
    if !q.contains(trigger.entity()) {
        // not a predicted entity, so don't interp
        return;
    }
    info!("Adding visual interp component to {:?}", trigger.entity());
    commands
        .entity(trigger.entity())
        .insert(VisualInterpolateStatus::<T> {
            trigger_change_detection: true,
            ..default()
        });
}
dull lion
#

I have a type alias called Displayed equal to Or<(Predicted, Interpolated)>, which wouldn’t suffer from this (and is also clearer imo)

wintry dome
#

Replicate being the component the server has, in case i want smoothing if the server has a gui

#

hmm, or perhaps just define RenderedEntity based on server/client features

#

this works nicely, thanks πŸ™‚

wintry dome
#

i've PRed a fix to the spaceships example in light of that

unkempt sedge
#

Oh my

#

My infra is finished

pine cape
#

all the examples in the repo are like that

unkempt sedge
#

oh yeah dumb question

wintry dome
#
impl MapEntities for DockingJointMarker {
    fn map_entities<M: EntityMapper>(&mut self, mapper: &mut M) {
        self.entity1 = mapper.map_entity(self.entity1);
        self.entity2 = mapper.map_entity(self.entity2);
    }
}

this is for a server->client replicated component – what i really want is to map the server entity to the Predicted entity on the client, not the confirmed entity. is that possible somehow?

#

(if i understand correctly atm this will map the server's entity to the client's Confirmed entity)

pine cape
#

What is the exact use-case?

#

In general the server entity needs to be mapped to the Confirmed entity, otherwise replication updates will get messed up

#

and then you could manually map from the Confirmed to Predicted

#

(which reuses the map_entities function you defined for your component)

wintry dome
#

i have a system that runs on server and client to detect a landing, and dock the player to the landing zone with a fixed joint. as seen in this comment: https://github.com/Jondolf/avian/pull/507#issuecomment-2328750872

here's the relevant system bit that creates the joint, which happens by creating a (replicated) DockingJointMarker which has a hook to insert the actual FixedJoint. I generate a suitable hash so it can be predicted by clients.

// DockingJointMarker is ChannelDirection::ServerToClient
//                       .add_prediction(ComponentSyncMode::Once);
//
// Actually creating the FixedJoint works like this:
// You spawn an entity with the DockingJointMarker component, along with a suitable
// PreSpawnedPlayerObject.
//
// Hooks in the DockingJointMarker component will insert the actual FixedJoint and do the housekeeping.
let joint_marker = DockingJointMarker {
    entity1: hit.entity,
    entity2: player_entity,
    rotation_offset,
    local_anchor1: joint_anchor_lz,
    local_anchor2: joint_anchor_player,
};

// joints are also predicted by clients, so must have a unique hash.
let joint_salt = joint_marker.hash_without_entities(player.client_id);
info!("Spawning joint with salt {joint_salt:?}");

let mut joint_entity_cmd = commands.spawn((
    PreSpawnedPlayerObject::default_with_salt(joint_salt),
    joint_marker,
));

if identity.is_server() {
    let replicate = ... 
    joint_entity_cmd.insert(replicate);
}

on the server, the joint's entity1/entity2 are the (canonical, only) entities for the player and the (eg) asteroid. on the clients, the joint is created between the Predicted player and Predicted asteroid entities. but the entity mapping means DockingJointMarkers which are replicated server->client end up referencing the client's Confirmed entities.

#

i suppose i can spawn them on the client referencing Confirmed entities, and lookup the predicted ones in the hook when spawning the joint, so it can handle confirmed entities coming from the server. wondering if there's an existing patter or something for this kind of thing

#
app.register_component::<DockingJointMarker>(ChannelDirection::ServerToClient)
  .add_prediction(ComponentSyncMode::Once).with_mapping_confirmed_to_predicted_entities();

the most useful api for my scenario i think would be something like that ^ which maps entities again when copying the component from confirmed to predicted entity, to translate confirmed to predicted entity ids. perhaps there's a different approach though? i can of course just do the mapping myself in the hook.
in "everything is predicted" client land i don't really ever want to refer to confirmed entities, since i'm simulating the predicted ones.

wintry dome
#

(i'm implementing it by manually mapping entities myself for now, will see if that brings enlightenment..)

wintry dome
#

got it working my using Confirmed entity ids in my marker component and translating as needed in the hooks. now clients can prespawn the same FixedJoints as the server using the PreSpawnedPlayerObject mechanism, and it syncs up. a little gnarly, but hooks came in handy. https://gist.github.com/RJ/5b803ce053f9c4b4c4726724877c4b3f

Gist

Docking plugin with client predicted/prespawned joint creation using lightyear - spacepit_docking_plugin.rs

pine cape
#

@wintry dome i'll try to take a look at this over the weekend. I didn't really plan for pre-spawned entities with components that refer other entities, cool that you got it working!

wintry dome
#

yeah it's a slightly unusual case. nice to be able to predict the joints on the clients though. if you do take a look i'd be interested if you can think of a better approach πŸ™‚

unkempt sedge
#

Question if I have two distinct packages with their own bins/mains, how can I make it so I can combine them together?

stiff quiver
unkempt sedge
#

I was trying to follow a workspace infra, but it seens I tried to fly before I could crawl. My ideas was to dissociate server and client. Into two different packages but it seens that required more configs than I expected

#

I dont know why but I can for the life of me, figure out why my gizmo is not being replicated in client

stiff quiver
unkempt sedge
#

yeah gonna follow that workflow

#

My brain is just too smooth currently

stiff quiver
#

@pine cape had this random idea, idk yet how feasible it is though.. what do u think of multiple protocols in one app? it seems like a neat logical feature since it would be possible eg to have different protocols for different platforms / transports or even things like a specific protocol just for "admin users".. smth like that. The question is if thats even feasible since the protocol is so tightly coupled into the app.. yea dunno

#

Especially inputs would be hard to do i think

stiff quiver
#

so that should at best be somewhat of an annoyance

summer finch
#

Hey, what's the correct way to handle PreSpawnedPlayerObjects that don't live long enough to receive the replicated entity from the server? E.g. a projectile that gets destroyed 1-2 ticks after creation.

Currently I have the projectile cleanup system running as a shared system, but it's causing plenty of

ERROR lightyear::shared::replication::receive: Received despawn for an entity that does not exist

If the projectile is alive long enough to start receiving updates from the server, the error does not happen

wintry dome
#

i don't know the answer.. could be worked around by removing the useful components and leaving PSPO and a DespawnMeIn(10: Tick) type component for cleanup i suppose

pine cape
pine cape
#

@wintry dome so i'm trying to understand the problem with the docking.
There's a shared system which:

  • creates a PreSpawned entity on client with DockingJointMarker which references the predicted entities
  • creates a PreSpawned entity on server with DockingJointMarker which references the server entity.
    The server-entity gets replicated to the client, with DockingJointMarker referencing the Confirmed entities. Thanks to the hash, we find the corresponding PreSpawned client entity and we add a Predicted-Confirmed link.
    I don't see where the problem happens, because the pre-spawned entity on the client should have the correct DockingJointMarker referencing other predicted entities
summer finch
# pine cape Hm is it causing some issues or is the only side-effect the extra logs?

There is definitely something in the projectile cleanup that's causing rollbacks, but I'm not 100% sure what yet (might be unrelated to the PreSpawned entities that get deleted early)
I've experimented making the cleanup a server-only system, and then there's some projectiles that never get cleaned up in the client. Could it be that the server-replicated entity is getting matched with the wrong prespawned client entity?

#

Honestly I'm a bit lost at the moment πŸ™‚ I'll start by getting some more data on what's causing the rollbacks

neon oak
pine cape
#

Yes some parts of the book are outdated; you can look at these 2 examples to follow along:

GitHub

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

GitHub

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

pine cape
summer finch
# pine cape yea networking/rollback issues are kind of pain to debug πŸ™‚

Yep πŸ™ƒ

By the way, I added rollback events in my fork some time ago so I could have some aggregate statistics on what entities and components are causing rollbacks (I couldn't find a way to do it before). I just made it into a small PR in case you'd like to upstream https://github.com/cBournhonesque/lightyear/pull/628

GitHub

Currently there's no easily accessible way of checking what components are causing rollbacks.
With this PR, we fire an event whenever a rollback check detects a mismatch which listeners can...

pine cape
#

Thanks! I looked at it but i think it would impact parallelism of the check_rollback systems; I think it might be better to just add log there in your fork

summer finch
#

Yeah, good point. I'll make a generic version for my fork with RollbackEvent<C>, but totally fine if you prefer to not upstream

unkempt sedge
#

Question how should I use the common apps from lightyear_common_examples

#

I am just doing a simple import from the lightyear_common_examples, and I keep getting a file error certificates.pem, specifically the big apps::new

unkempt sedge
#

Oh it seens it should generate new certificates, perhaps it should generate long term ones

stiff quiver
unkempt sedge
#

okey dokey

unkempt sedge
#

Question the workflow to load a bunch of assets should be, client loads assets. Server replicates those assets for all too see. Correct?

pine cape
#

Server replicates some marker component. Upon receipt, the clients load the asset

wintry dome
# pine cape <@140137915270430720> so i'm trying to understand the problem with the docking. ...

it has to support the case where the client can't predict the joint, so there's no prespawned client entity. in that case, the client will receive a replicated entitiy from the server with a DockingJointMarker, containing entities mapped to confirmed entities. this should work because the on_add hook for the DockingJointMarker converts them to predicted entities to use for the FixedJoint. (since the physics joint is between Predicted entities only on the client).

unkempt sedge
#

Ah this banana is way harder than I expected

unkempt sedge
#

Ah I guess I need to utilize componentinsertevent

#

oh man the wheels in my brain are starting to turn

wintry dome
#

i'll do some more network testing this week and check if the docking stuff behaves properly. i still occasionally get unexplain bouts of rollbacks I need to chase down

lone silo
#

how well does bevy-tnua work with lightyear?

unkempt sedge
#

And if you take in consideration the amount of alignment in in the 3d avian example probably lots of conflict, but perhaps you can mess around specific configs

summer finch
# summer finch There is definitely something in the projectile cleanup that's causing rollbacks...

Okay, going back to this rollback issue, I think I've narrowed it down enough to share: I might be running into an edge case bug with PreSpawnedPlayerObjects, probably.

To summarize: my game is using projectiles that are really fast moving. There projectiles are pre-spawned by the client that shoots. Whenever the projectiles are stopped, they get deleted that frame.
There's three types of behaviors I'm seeing with this, two of which are failure points:

  • If the projectiles get destroyed really early (in the video example, when I shoot the wall I'm in front of) no rollbacks are triggered but an error is printed
ERROR lightyear::shared::replication::receive: Received despawn for an entity that does not exist

This error gets printed by both clients.

  • If the projectiles get destroyed late enough, there's no errors and no rollbacks (this is the case where everything works correctly!)
  • There's a middle ground between the first and second case where no errors are printed but a rollback is triggered in the client that fired the shot (this should not happen) on some of the projectile components (which component this is triggered for changes per game boot) due to a missing history_value in the case where the confirmed entity exists

You can see all of these cases in the left side client in the video attached.

GitHub

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

#

I've got a workaround (adding a delay of 30 ticks to projectile cleanup gives lightyear enough time to catch up, and fixes everything), but I thought I'd share in case anyone else runs into this

pine cape
pine cape
# wintry dome i added the 3 main networking scenarios and how they should work to the comments...

So I still don't get some things:

  • A: client should spawn DockingJointMarker with predicted entities. Server spawns the DockingJointMarker which is replicated. The hashes match, the predicted-confirmed link is added
  • B (client doesn't predict a joint). Server spawns the DockingJointMarker. It gets replicated with confirmed entities. A predicted version is spawned, where a DockingJointMarker component is also added. Normally this line should be converting the component from Confirmed to Predicted entities. So everything should work. Maybe there's a bug here?
  • C (client predicts the joint but server doesn't): at the next rollback, the client DockingJointMarker should be removed

So normally you shouldn't have to do anything and you should just use the predicted entities directly on the client; everything should be handled for you

GitHub

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

pine cape
# summer finch Okay, going back to this rollback issue, I think I've narrowed it down enough to...

Thanks, that's super helpful. I opened an issue: https://github.com/cBournhonesque/lightyear/issues/631
SpawnTick is a component from your protocol?

GitHub

Okay, going back to this rollback issue, I think I've narrowed it down enough to share: I might be running into an edge case bug with PreSpawnedPlayerObjects, probably. To summarize: my game is...

#

In general I think unit-tests related to pre-spawned-object need to be added

#

it's one of the least tested parts of the codebase

pine cape
summer finch
floral meteor
#

shortly after this, I added a console command to test authority reassignment. I can confirm that the server correctly recognises the authority transfer, and so do the clients (their HasAuthority components shift around), but the Confirmed and Replicate components on each client don't line up. to clarify:

  1. Client1 spawns a vehicle and replicates it to the server. The server replicates it to Client2, ensuring that Client1 has authority over the vehicle.
    a. Client1 has one entity for the vehicle. It HasAuthority, it has Replicate, it does not have Confirmed.
    b. Client2 has two entities for the vehicle. Neither entity has Replicate or HasAuthority. One entity has Confirmed. This entity is what's rendered.
  2. So far, so good. On the server, I then transfer authority of the vehicle to Client2. This is where things start to break down:
    a. Client1 still has one entity for the vehicle. It does not have HasAuthority, but it still has Replicate and it doesn't have Confirmed. That is, it knows it doesn't have authority, but it's not receiving sync.
    b. Client2 still has two entities for the vehicle. Neither entity has Replicate. The first entity HasAuthority and Confirmed. The second entity has neither. That is, it knows it has authority, but it doesn't appear to be sending sync.
  3. This leads to an inconsistent state where neither client is sending or receiving sync, as far as I can tell.

the questions I have are:

  • should the Confirmed and Replicate components on each client be automatically updated when authority transfer occurs?
  • should a second entity be spawned when a client goes from sending to receiving?
  • is the root issue that the second client isn't sending sync, so the first client doesn't know it should be receiving sync?

(an immediate answer isn't necessary - I'm working right now - but I figured I'd write this up now so that you can answer at your own convenience)

pine cape
# floral meteor shortly after this, I added a console command to test authority reassignment. I ...
  1. b. How come client2 has 2 entities for the vehicle? One is Confirmed, one is Interpolated? The Interpolated one is rendered?
  2. a. That's good. Client should still keep the Replicate bundle, but it's not sending sync updates because it doesn't have authority.

should the Confirmed and Replicate components on each client be automatically updated when authority transfer occurs?

Replicate is not replicated on design, because you might not want to have the same replication settings when transferring authority. (especially when transferring from server to client).
You should instead add the Replicate bundle on client 2; it won't send replication updates until you first get authority.

Confirmed isn't updated at all either, currently. This requires deeper thinking, but Confirmed is usually just a marker on the client to distinguish between an Interpolated/Predicted entity and the entity that is actually receiving replication updates (Confirmed).
I assume on your server Replicate you have something like SyncTarget.interpolated = NetworkTarget::All? Then when client 2 has authority, the client 1 should spawn a second entity with Interpolated.

should a second entity be spawned when a client goes from sending to receiving?

No, if you're not doing prediction/interpolation; you should have only 1 entity per peer at all times.

is the root issue that the second client isn't sending sync, so the first client doesn't know it should be receiving sync?
Yes the root cause is that the second client doesn't have Replicate, so the server isn't receiving any updates from client 2 that it can propagate to client 1. You can see in this example that all clients have Replicate

GitHub

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

wintry dome
pine cape
wintry dome
#

yeah i removed the now useless manual mapping, simplifies things a bit

floral meteor
unkempt sedge
#

Anyone knows if lightyear has good compat with rapier?

pine cape
#

I haven't tried it

wintry dome
#

i expect managing physics rollback will be much harder with rapier, since it doesn't use the ecs for storage of physics values like avian does

#

or perhaps easier if you can cheaply clone the physics world πŸ€”

#

@summer finch i have some rollback stuff to debug, what state is your rollback metrics thing in? i like the look of the egui pane showing aggregate stats from your video, would love to give it a try

unkempt sedge
#

What is the flow for prs?

#

THe lobby example could use a little update

pine cape
#

Feel free to make a PR, I (or others) will review it when I can

unkempt sedge
#

gave it a shot, question why is the lobby example based around bevy egui? And not native bevy ui ,is it because of the upcoming refactor?

unkempt sedge
floral meteor
# pine cape 1. b. How come client2 has 2 entities for the vehicle? One is `Confirmed`, one i...
  1. Yep, that's right. I was initially rendering Confirmed, but I've switched it over to Interpolated
    2.a. gotcha

You should instead add the Replicate bundle on client 2; it won't send replication updates until you first get authority.
alrighty, I've added a system that attaches Replicate on the addition of HasAuthority. that seems to work - the server can now see the synced data from Client 2.

acknowledged on the other points, that all makes sense.


the issues I'm seeing now are:

  • I have a server system that attaches the Replicate bundle when Replicated changes; however, Replicated doesn't appear to change when authority is transferred and the new peer is sending sync (I can confirm that the sync is now coming from client 2); this means that the new Replicate bundle is never attached, which is probably why client 1 isn't seeing sync

  • Client 2 has two entities, as expected; its Confirmed and Interpolated entities. my rendering* is done against the Interpolated entity when it doesn't have authority; when it does have authority, though, HasAuthority is attached to the Confirmed entity, which means both entities end up being rendered* (one has HasAuthority, the other has Interpolated).

    I believe this is part of why I was using Confirmed to begin with, so that there's only ever one entity that gets considered for tx/rx sync. I'm not sure how to best deal with this while still using interpolation; I can disable it, and that's probably fine for my application (I'm using physics as a form of interpolation), but it'd be nice to keep it working if possible

*: "rendering" isn't quite what I'm doing; I'm attaching a custom representation if it's Interpolated or HasAuthority, and it's non-trivial to transfer that representation between entities

P.S. no stress on this issue - I recognise it's the interaction between at least three complex features (client spawning, authority transfer, interpolation), and that my setup is pretty convoluted by itself

stiff quiver
#

uh two question regarding interest management:

  1. apparently when using interest management, when despawning entities the server asks the client to despawn entities that arent replicated to it (they arent in the same room). i think this only happens after they have been added to the room once, anyone had this issue before?

  2. any solutions to having interest management in host server mode? my best guess would be using the clientside replicated marker component (nvm that, using With<Replicated> doesnt seem to work)

pine cape
#
  1. You mean that client 1 joins room A and then exits it.
    When an entity E1 in room A gets despawned, a despawn message is sent to client 1, even though it shouldn't be the case?

  2. You mean how to restrict the visibility of the host, who can currently see everything?

stiff quiver
stiff quiver
#

otherwise i guess i could handle that by having some kind of marker that i add whenever making an entity "visible" for the virtual client

stiff quiver
pine cape
pine cape
stiff quiver
#

sounds like a bug to me

stiff quiver
summer finch
wintry dome
unkempt sedge
#

Is it possible to reflect replicated resources?

pine cape
#

You can just enable reflection on the resource when you define the type

summer finch
# wintry dome that would be great, thanks πŸ™

Alright, here's the links. It's quite heavyweight to use since I never really planned to share this, but i'll explain as best I can

  • https://github.com/mbrea-c/bag_of_holding: Has a couple of helper crates for working with lightyear:
    • ZusammenPlugin I found myself repeatedly writing *Protocol, *Server, *Client and *Shared variants of plugins with similar configurations for using with lightyear, so I made this meta-plugin crate that encapsulates it in a single struct (and that way I only need to configure it once)
    • ZusammenApp framework for running a multiplayer app given a ZusammenPlugin encapsulating the game logic and some basic configuration. I built this to abstract away some lightyear setup boilerplate, at the cost of making assumptions about the app (It doesn't support HostServer mode for example, as I don't really use it). Heavily adapted from the common part of the lightyear examples
    • buzzdebug Debugging framework for lightyear using egui. You implement LocalDebugModule for a module that collects data from the client and displays it in the Buzzdebug egui window with the provided render function. RemoteDebugModule works similarly except the systems that collects the debug data runs in the server and the collected resource is replicated to the client, where it is displayed in the debug window (yes I got tired of getting confused about what was going on in the server). A DebugZusammenPlugin is provided that takes a list of LocalDebugModule and RemoteDebugModule and sets up all systems, replication, etc.
  • https://github.com/mbrea-c/lightyear/tree/mbc/rollback_event: My lightyear fork with the RollbackEvent and rollback cause aggregation stuff.
  • Attached to this message is the lightyear LocalDebugModule that prints out to egui the rollback cause aggregation data (it's from another private repo)
#

So, there's two ways you can use this:

  • Option 1, ignore all that nonsense and just use the lightyear fork. The AggregatedRollbackCauses resource contains all the aggregated data you need, you can print/render it out however you want.
  • Option 2, create a DebugZusammenPlugin with the lightyear debug module attached
        let debug_plugin =
          DebugZusammenPlugin::new().with_local("Lightyear (client)", ClientLightyearDebugModule);
    
    and run the .add_protocol ,.add_shared , .add_server, .add_client in the Protocol, Shared, Server and Client plugins respectively. E.g.:
     impl Plugin for ProtocolPlugin {
       fn build(&self, app: &mut App) {
         // ...stuff
         let debug_plugin =
           DebugZusammenPlugin::new().with_local("Lightyear (client)", ClientLightyearDebugModule);
         debug_plugin.add_protocol(app);
       }
     }
    
GitHub

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

#

here's how one would create a multiplayer app with the zusammen app
thingy, if anyone's curious (maybe I'm just showing off a bit):

fn main() {
    let debug_plugin =
        DebugZusammenPlugin::new().with_local("Lightyear (client)", ClientLightyearDebugModule);

    let plugin = CombinedPlugins::new()
        .and(CharacterZusammenPlugin)
        .and(debug_plugin)
        // add more zusammen plugins here with actual game logic :)
        ;

    run_multiplayer_app(ZusammenAppConfig {
        plugin: Arc::new(plugin),
        mode: ZusammenAppMode::Host { port: 1337 },
    })
}
#

And yeah the naming scheme is... they were never meant to see the light of dayferris_sob

#

After writing this up I realize it's probably a lot of friction to set up, probably taking option 1 and just logging the aggregated rollback causes will be faster for some quick debugging

unkempt sedge
#

poggerrs

#

@pine cape Btw I am finishing up the whole pretty lobby ui system of my game thankerino for u examplerino

floral meteor
#

by the way, on the subject of interest management: my plan is to calculate what each player can see on each interest-management-tick by using a KD-tree to locate nearby entities.

what's the best way to do this? do I manually maintain the entities that they can currently see so that I can lose_relevance for them once they leave, or can I directly control the set of visible entities? (I'd prefer to do this if possible - I suspect it'd be a lot cheaper than keeping track of the state myself and issuing potentially many calls to the *_relevance functions)

wintry dome
stiff quiver
pine cape
# floral meteor by the way, on the subject of interest management: my plan is to calculate what ...

There's this struct that contains for a given entity the list of clients that can see the entity, but I don't think I have the reverse (for a given client, get the list of entities that are visible for that client).

The main reason is that the replication loop goes through each individual entity that has ReplicationTarget and then uses that data to decide which clients to replicate to. Maybe there's a way to change the logic there..

So unfortunately I think calling *_relevance might be your best bet right now? If I understand correctly, it is inefficient because you would have to do a range-search every tick and then compare with your previous value to find out the points that gained/lost relevance?

GitHub

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

pine cape
stiff quiver
# pine cape I guess you could use a `room` to store the list of entities visible to a given ...

uh idk, i just did it like this:

fn system_update_camera_views(
    q_objects: Query<(), With<ObservableMarker>>,
    q_transform: Query<&GlobalTransform>,
    mut q_camera: Query<(&ViewRange, &GlobalTransform, &ObservationRoom, &mut LinearVelocity, Option<&ObservationAnchor>)>,
    mut room_manager: ResMut<RoomManager>,
    intervals: Res<Intervals>,
) {
    for (view_range, cam_transform, cam_room, mut lin_vel, anchor) in q_camera.iter_mut() {
        if let Some(anchor) = anchor {
            let transform = q_transform.get(anchor.entity.entity()).unwrap();
            lin_vel.0 = cam_transform.translation().truncate() * -0.1 + transform.translation().truncate() * 0.1;
        }

        let cam_aabb = Aabb::from_half_extents(
            cam_transform.translation().truncate().into(),
            Vector::new(1920.0 / 1.5 / view_range.0, 1080.0 / 1.5 / view_range.0),
        );

        let mut viewed = find_intersections(&intervals, &cam_aabb);

        // TODO check for Option<&Opacity> > Some(0.0) || 1.0
        viewed.retain(|e| q_objects.contains(*e));

        let prev_viewed = room_manager.room(cam_room.0).entities.clone();

        prev_viewed
            .difference(&viewed)
            .for_each(|x| room_manager.remove_entity(*x, cam_room.0));
        viewed
            .difference(&prev_viewed)
            .for_each(|x| room_manager.add_entity(*x, cam_room.0));
    }
}
#

ignore the specifics to my app obv

stiff quiver
floral meteor
# pine cape There's [this struct](https://github.com/cBournhonesque/lightyear/blob/0bdf9bee4...

yup, that's my concern - I'd have to track which entities my players could already see and compare them to the new set, and then call *_relevance, which seems like it'd be duplicating state that Lightyear already has.

that being said, if Lightyear's internal implementation is actually "inverted" - that is, the entities keep track of which players see them, instead of the other way around - I can understand why the relevance functions make sense, and I'll go ahead with that plan for now

#

rooms could work - that's what I was initially considering when speccing this out a few months ago - but I'd like to also do virtual worlds on top of distance-based interest management in the future, which might not work super well

wintry dome
#

@summer finch extended your rollback cause stuff to also trigger an event on entities that cause rollbacks, which i observe in my renderer plugin to add visual indicators:

pub fn aggregate_rollbacks<C: Component>(
    mut causes: ResMut<AggregatedRollbackCauses>,
    mut evr_rollback_event: EventReader<RollbackEvent<C>>,
    tick_manager: bevy::prelude::Res<crate::prelude::TickManager>,
    mut commands: bevy::prelude::Commands,
) {
    for ev in evr_rollback_event.read() {
        causes.new_rollback::<C>(ev);
        // trigger an event on the entity that caused the rollback
        let caused = CausedRollback {
            tick: tick_manager.tick(),
            component: type_name::<C>().to_string(),
            reason: ev.reason,
        };
        commands.trigger_targets(caused, [ev.entity]);
    }
}
summer finch
#

I'll add it to my fork later today

inner hill
#

this is a minor thing but I wonder if we could automatically generate protocol ids to a certain extent

#

basically generate a hash of all registered components/their configurations

stiff quiver
inner hill
#

ya, but do non-sent components really matter for a protocol id?

#

o, I don't mean register_type components, I mean specifically registered for replication

stiff quiver
#

i mean yea i guess you could do that but that would require components to implement hash, no?

inner hill
#

no no, you can do it on TypeId I believe

#

only the configuration of it would require hashing

#

im not super familiar with the internals but stuff like ChannelDirection and stuff

stiff quiver
inner hill
#

ahh true I forgot about that limitation

#

hmmm

#

abuse type_name like bevy? lol

stiff quiver
inner hill
#

yeah I'm pretty familiar with that, when I made my own networking library a while back I ended up making a file that created consistent type ids for each registered component

stiff quiver
inner hill
#

technically type_name is also not supposed to be used like this according the rust team

#

they make no guarantees

#

but bevy reflect stuff just decided "naw fuck that"

stiff quiver
#

i mean yea but what should happen? a name is a name, its not like the compiler would randomly decide to rename a struct or smth

inner hill
#

so far it hasn't changed but its just the lack of standardization so bevy does it anyways

#

I think what would happen is stuff like trimming the name or changing the path of it

#

not necessarily the end type name

#

so over time it might not match up exactly

stiff quiver
#

That should only happen across compiler versions, not compilations tho

#

if anything

#

so it wouldnt be a major issue i think

inner hill
#

ya it shouldn't be

#

any change they make probably wouldn't break using it for protocol ids anyways, worst case is the protocol id changes unnecessarily

stiff quiver
#

relying on hard coded names is obv not a good idea tho xd

inner hill
#

unless you get quirky with it and start randomly naming things

stiff quiver
#

i wonder how they are gonna deal with that once bsn etc are out

#

relying on reflection for that is prob not a good idea

inner hill
#

eh, reflection is fine for it I think

#

not the best, but if you are reading these files from disk that performance hit will be the least of your concerns

inner hill
#

Is there any reconnection handling? Like a way to erase sent parts of the world to a specific client

pine cape
#

I think when a user disconnect I despawn every replicated entity

inner hill
#

hmm lemme try some stuff to verify but i think the client didn't receive older updates if the app was restarted

stiff quiver
#

steam probably too

unkempt sedge
#

You know what would be cool a matchmaking style example

stiff quiver
unkempt sedge
#

kinda of, but like is more how to self host and create client side lobbies

#

Matchmaking is more of the idea of click button to matchmake -> create server sided lobby -> connect available clients

#

Is simplerish and is the goto of most competitive games

stiff quiver
#

hm ok

unkempt sedge
#

Also there is the idea of a server db, that stores character points and suchh

stiff quiver
#

tho ig it wouldnt be much different from how regular bevy does it

unkempt sedge
#

How can I selectively transition the state of specific clients ids? Is worth noting my host will always be my server

#

Like only client x and y, inside lobby should have their state converted to in_game

unkempt sedge
#

messages only apply to certain clients................

#

There is even a client arg

thick lily
#

What should I do to postpone starting replication to new clients (let's say we download a rather heavy map from URL provided by server and don't want anything weird happening on client while we're loading)? Is creating a room for "ready" players the only way? Can I exclude a specific client from recieving any messages on given replication group(s) without that?

pine cape
#

Yes there is a function send_message_to_target where you can select the clients you want to send a message to

#

And for component replication you can update the ReplicationTarget component

thick lily
#

I mean I shouldn't really modify (can I even?) ReplicationTarget of all my replicated entities on the server just because someone attempted to join (connected and sent server a message "hey! what's the current map?")

pine cape
#

Yes maybe the best is through InterestManagement to control who is receiving replication updates

fervent karma
#

Hi, I'm following the guide to set up lightyear, and while it is a bit outdated I managed to work around that, but the guide suggests using a link conditioner, presumably to ensure the game works well under more realistic conditions, and I kept that on, but I kept seeing a weird delay even after enabling prediction, and I managed to track it down to being caused by the link conditioner's incoming_latency parameter. It doesn't seem right to me that this should affect the input delay for a client's own predicted entity, so have I set up prediction incorrectly, or is there something wrong in the way the link conditioner works?

#

Also it seems to be affected by both the client and server link conditioner settings, and more strangely is that the prediction takes N ms to start (where N is the sum of the delay from the client and server), but after that initial period the prediction is very snappy, and responds to my inputs immediately

unkempt sedge
#

@pine cape Check this out boss it working my server administrated matchmaking

pine cape
fervent karma
#

Alright, I'll try running the example directly

stiff quiver
stiff quiver
#

@pine cape ive been looking into WebRTC / p2p in general in the past few days. Given lightyear moving away from serverauthorative a bit (ie. Authority transfer, Steam Sockets, ..) is that something worth looking into for implementation in lightyear or just a waste of time (incase you don't like the idea of having p2p capabilities in the first place)?

#

I think from the technical side its not even that difficult to implement given that there are existing implementations of a similar style (naia, libp2p, webrtc-rs, ..)

fervent karma
unkempt sedge
#

is the maximum my raspberry server can handle πŸ™‚

unkempt sedge
fervent karma
#

Yeah, I've got prediction and interpolation set up in the same way, it's just not working as it should (but only in my version). I'll keep looking at it though, I probably just missed something silly

fervent karma
#

Okay I fixed it, turns out it was just a bevy system order problem πŸ˜‚ ControlledBy didn't fix it, it was buffer_input.in_set(InputSystemSet::BufferInputs)

#

Turns out it was all due to me reading the guide incorrectly πŸ˜‡

fervent karma
#

What's the accepted way of hiding the Confirmed objects when predicting or interpolating? It seems that when the object is initially replicated there is no Confirmed on it, so I can't check for something like

confirmed
    .map(|confirmed| !(confirmed.predicted.is_some() || confirmed.predicted.is_some()))
    .unwrap_or(true)

I tried searching, but I can't find anything

dull lion
fervent karma
#

Oh, but what about things that aren't predicted or interpolated? Or are all entities one or the other?

dull lion
#

If it’s replicated it should be one or the other

fervent karma
#

But some objects are neither replicated or interpolated (not that I have any rn, but in theory some objects are)

#

But I'll do that for now, thanks!

dreamy silo
#

any clue here why when i speed up my rocketship and get far away from the star cluster at 0,0,0 it seems laggy? any advice on things i can check to figure out what the issue is? if I drop the star field it consistently holds 60 fps but i still get laggy behavior the further away i move from the center. I can assure that the follow_camera and player movement system is in the same schedule, FixedUpdate.

#

not sure what it could be. I can see similar lag on the server. so not sure if it could be fps related really.

#

Linear andAngular velocity together with position and rotation all have "ChannelDirection::ServerToClient" and "ComponentSyncMode::Full"

thick lily
pine cape
stiff quiver
#

idt the surface level api would change at all, it would basically just be the transportation layer getting a new option

pine cape
#

How would the new option looked like compared to host-server mode? I think most of the lightyear replication logic needs a server

stiff quiver
dreamy silo
#

since its moving based on position

dreamy silo
pine cape
dreamy silo
# pine cape No i don't think you need to register LookTransform. But maybe check the orderin...

my bad for not trying more different things on the camera i had. actually i think i figured it out now. the lag_weight on my smoother camera was at 0.9. when i put it to 0.01 there is minimal lag. might make sense with it seeming laggy the higher the velocity got. the remaining lag now seems to be fps related cuz when i move out of the star field fps improves, goes from 25-30 to 38-42. but thats something i just gotta work on. the stars are each a entity. gonna try and see if i can replace them with a background instead.

#

actually scratch what i just said. didnt make any sense anyways. iΒ΄ll have to debug some more. will take a look at the ordering of my systems.

#

maybe i need to do extrapolation instead? asked AI using the files as context, and this is what its saying:

" The shaking of the spaceship when it picks up speed is likely due to a mismatch between the physics simulation, network replication, and visual interpolation. Here are a few reasons this might be happening and some suggestions to fix it:

  1. Interpolation vs. Extrapolation:
    The VisualInterpolationPlugin you're using is designed for interpolation between known states. When an object is accelerating, interpolation can cause it to lag behind its true position.
    Fix: Consider using extrapolation for fast-moving objects. You might need to implement a custom extrapolation system or modify the existing interpolation system to include velocity in its calculations."
#

something i did to test if its the follow_camera or not thats "lagging behind" is to take it completely off schedule and zoom out. the shaking seems to go away. so probably need to put follow_camera closer to the action state/player_movement, if that makes sense.

dreamy silo
#

Hold on a minute, if bevy runs all systems inside a tuple in parallel, the executions might seem off at times. Maybe adding a .after condition helps. I’ll try that when I’m back on my laptop

#

add_system(handle_input) .add_system(camera_track_player.after(handle_input))

Or

.add_systems((handle_input,camera_track_player).chain())

#

Perhaps instead only run the system when Position is updated, using Updated<Position> etc

inner hill
inner hill
#

does an entity with both interpolation and prediction even make much sense?

#

since prediction/interpolation are both just for rendering as far as I can tell

pine cape
unkempt sedge
#

I need to save the current character loadout, I am guessing it should be in server

pine cape
#

That should be independent from lightyear no? I think you could do either client or server

stiff quiver
inner hill
#

Well it all just depends on authority there too. Do you want the player to be able to say their loadout is whatever they want?

#

if its some innocuous stuff you just send to the server anyways as a "loadout config" its fine on client, if its a small game that you dont care about cheating then client is also fine there. Main thing is just if there is something in the loadout you don't want the player able to do

stiff quiver
inner hill
#

well character loadout to me implies more of a thing like abilities/inventory/gear/etc.

stiff quiver
pine cape
#

Thanks, would be good to benchmark these, I don't think channels are the bottleneck though

stiff quiver
#

not really yea but its always good to be faster :p

stiff quiver
pine cape
#

@pine finch did you have a question? :p

pine finch
#

Is there a way to 'suspend' replication of an entity for individual clients?

I've got a scene with 10,000 entities doing their own thing split into rooms based on what's in a client's frustum and entity commands to add/remove entities seem to be very expensive (at least bandwidth wise) so I wonder if there's a way to pause component updates up to a certain point rather than outright removing the client from the room thus deleting the remote entity.

Soz for earlier, hit enter prematurely :>

pine cape
#

I don't think this is possible now. You can suspend replication of an entity to all clients, but not to an individual client

#

But yea that seems like something that could be useful; some kind of configuration in the visibility handling, so that when the entity stops being visible, we stop sending updates instead of despawning/respawning the entity

pine finch
#

I also assume that adding and removing replicated marker components too often is a bad idea :p

#

Used to add entity states with marker components but I was getting humongous bandwidth spikes so I switched to an enum and everything smooth now

pine cape
#

You shouldn't have to add/remove the Replicated component, that's mostly for lightyear internals

#

you can remove/add the Replicating component to pause/resume replication

pine finch
#

Ah, I meant I was adding and removing a component that I registered (eg. EntityTarget(Vec2)) to define a movement target for the entity

pine cape
#

ah i see

pine finch
#

(It probably shouldn't have been included in the protocol anyway, but I was thinking of using it for interpolation later)

dreamy silo
# dreamy silo correct this is enabled. im using smooth-bevy-cameras crate to have the camera t...

Just wanted to comment back on this in case others encounter similar issues. My low key suspicion was correct. The issue is that the smooth_bevy_camera crate with the LookTransformPlugin is running in the Update schedule, so when I use the component and the plugin, the system for its component is running in Update, not FixedUpdate schedule like I actually wanted my fn camera_follow system to do. Removing the LookTransform component entirely from my camera set fixed most of the issue for me. Most of the jittery and stuttery has now gone away. Fantastic! Figuring this out might have given me the lesson I needed on schedules, sets and conditions that I need to tackle similar issues. I’ll write a PR or an issue for the crate mentioned above explaining the need to disable it from the update schedule and adding it to the correct schedule if it’s different from the one one would put physics and movement in. Basically the same thing that’s done in the spaceships example in the src/shared.rs file , where SyncPlugin disabled from fixedupdate and instead moved to PostUpdate schedule. Gotta make sure the stuff you’re adding and running from another plugin isn’t stuck in another schedule in the background.

desert saffron
#

I noticed a common use case that requires writing lots of boilerplate that lightyear can potentially abstract over.

There are some components that can't and probably shouldn't be replicated (Collider) being one example. Atm, you have to attach a marker, detect when its replicated on the client, and add the same components again.

#

I am proposing maybe theres some #[derive(NetBundle)] or something equivalent which lets you have different components auto-initialized on replication by some trait, say default

#
#[derive(NetBundle)]
struct PlayerBundle {
  id: PlayerId,
  color: PlayerColor,
  #[client_init(create_player_collider)]
  collider: MyCollider
}

Perhaps something like this? Where you spawn the bundle on the server, and once its replicated to the client the client will use that function to initialize it?

#

or maybe some abstraction over hooks makes more sense

pine cape
#

So when you detect that a PlayerId marker component has been replicated to the client, you add the MyCollider component on the client?

#

I think it might make more sense for users to provide their own observe/hooks/systems to do that kind of stuff

unkempt sedge
#
fn listener_search_match(
    mut events: EventReader<MessageEvent<SearchMatch>>,
    player_ids: Query<(Entity, &PlayerId), With<PlayerId>>,
    mut online_state: Query<&mut PlayerStateConnection>,
) {
    for event in events.read() {
        let client_id = event.context();

        let id_to_compare = PlayerId(*client_id);

        for (entity, player_id) in player_ids.iter() {
            if *player_id == id_to_compare {
                info!("This player is searching for match {}", client_id);
                let mut on_state = online_state.get_mut(entity).expect("I a mtired boss");
                *on_state = PlayerStateConnection {
                    online: true,
                    searching: true,
                    in_game: false,
                }
            }
        }
    }
}```
#

Does anyone know a better way of changing components when a server receives a message? Like an alternative in each i dont need to iter through basically all players.

stiff quiver
unkempt sedge
#

but what would be the key to that hashmap?

#

oh god nulled

#

why is my brain like htis

stiff quiver
desert saffron
#

Hey all, are there any known bugs with the LeafwingInputPlugin?

desert saffron
#
2024-09-25T02:22:50.559458Z ERROR lightyear::connection::netcode::client: client ignored packet: invalid packet: sequence 88203 already received
#

whenever I add the input map, I get this invalid packet spam

#

and sometimes the server crashes too

#

this happens even if I dont use the inputs in a system

pine cape
#

That is the replay protection system, where I check if a given packet was already received. Are you testing with very high latency?

desert saffron
#

No like 80ms in the conditioner, but I tried without the conditioner and still had issues. This is a localhost connection.

desert saffron
#

If I remove InputMap. everything works fine but as soon as I add that to the player entity the replay protection system spams

desert saffron
#

if I use another transport like wasm + websockets, I dont get that replay protection spam but I do get the same broken physics before

#

even if I dont use the input map in any systems, adding it breaks my cube and it gets stuck floating in the air

pine cape
#

I think there must be some other issues beside leafwing that's causing your problems; the leafwing plugin is pretty well tested

#

What was the input config you were using?

desert saffron
#

the default

#

I just can't come up with an explanation for why physics breaks as soon as I add the input map to the entity, even if I dont send any inputs

river perch
#

Hey all, I am having trouble adding a Scene to a replicated a character from server to clients:

  1. clients connect to host server
  2. server spawns in an entity for each client (including self)
  • each entity has a player id component, SpatialBundle, Replicate, PrePredicted
  • this seems to work fine
  1. client has a system that queries for Added<PlayerId>, and inserts a Handle<Scene> to that entity, which is from a gltf that contains the mesh, armature, animations, etc.
  • On the host client, this works fine, and I can see one character model for each client, and move them around, etc.
  • but on the client, I see a deformed mess of the model, and the transform doesn't seem to be replicated properly. There are also ~350 entities spawned in on the non-host client, most of them just have an Interpolated component.

Right now my protocol only has PlayerId and Transform copied ServerToClient.
Has anyone worked with Scenes in this way before? I couldn't find an example that uses them instead of Pbr for example. Thanks!

river perch
# river perch Hey all, I am having trouble adding a `Scene` to a replicated a character from s...

Hmm, I'm now also adding an entire SpatialBundle to the client models, instead of just Transform. That seems to fix the visual of the model. I am concerned still that there are ~350 entities spawned only on the non-host client though. Those only appear after I add the Handle<Scene> to the models on the host server I think. Is that normal to have so many entities with just an Interpolated component? They don't exist on the host client, so I don't understand what they are interpolating

desert saffron
pine cape
river perch
onyx anvil
#

Do I understand correctly that currently lightyear transfers all entity changes to e.g. position over the network?
is there a feature to enable desync detection with e.g. hashing the data and then only send entity updates when necessary to recover from a desync?

stiff quiver
#

it essentially uses a lightweight version of tcp over udp (look up netcode standard if u wanna know more)

onyx anvil
#

I'm wondering because the avian_3d_character example transfers several KB/s, even when no inputs are pressed

stiff quiver
#

havent looked at that particular example but i could imagine that it transfers replication authority over the character to the client

onyx anvil
#

this is with cargo run -- client-and-server

stiff quiver
#

If thats serverbound then its likely clientauthorative replication

onyx anvil
#

looking at RUST_LOG="lightyear::client::connection=trace" cargo run -- client-and-server and logging the decoded messages in src/client/connection.rs:435 I can see that the client receives a lot of messages of this form:

Got EntityUpdatesMessage updates=EntityUpdatesMessage { group_id: ReplicationGroupId(1), last_action_tick: Some(Tick(32)), updates: [(Entity { index: 19, generation: 1 }, [b"\x05\x02\0\0\0\0\0\0\0\xda\xf6\x9c4", b"\x06\0\0\0\0\0\0\0\0\0\0\0\0", b"\x07\x9d\x90\0@\x90\xff\x9f?7\x8cl5", b"\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80?", b"\x0f\0\x02\x01\0\0\x03\x03\x03\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"])] }

I assume this directly updates some data related to the entity. However, if the client correctly predicted the result, then this message should not be necessary, no? Thus, I suppose either lightyear always sends these fairly large EntityUpdateMessages (regardless of whether a desync happened or not) or the client mispredicts a lot

river perch
pine cape
pine cape
onyx anvil
#

Further, increasing the replication interval it becomes increasingly obvious that the server's and client's physics simulation are diverging, and it looks more and more glitchy/twitchy. It's especially noticeable when jumping on top of a block.

pine cape
#

Do you have prediction enabled? There might be a misunderstanding here; you can try setting the replication_interval to 10s in the simple_box example, and the client's cube will still remain completely smooth. That's because we apply the local client inputs immediately, and possibly do corrections only when the server updates reach the client. (And there shouldn't be any corrections if you only have 1 player)

#

Desync detection is usually used if you're only replicating inputs (as opposed to what lightyear is currently doing, which is replicating the state of the world directly)

onyx anvil
#

you can see how at 0:21 all entities teleport to different locations

dreamy silo
#

I’ve converted the spaceships example to be 3d instead and currently not experiencing any significant diverging between server and client

dreamy silo
onyx anvil
#

I tried the spaceships example and also could not get it to desync, even with a replication interval of 10s

dreamy silo
#

How do you get it to desync?

onyx anvil
#

could not

#

typo

#

sry

stiff quiver
#

With avian systems i mean

onyx anvil
#

This is why desync detection would be nice. ggrs has a synctest mode where it rolls the simulation back a few ticks every time and checks if you still get the same result.

stiff quiver
floral meteor
#

do resources map entity IDs?

unkempt sedge
#

Any tips on how to handle animation state in lightyear?

stiff quiver
stiff quiver
#

unless it interacts with the world then u would want it to run serverside

floral meteor
#

I think I'll just do a little sneaky and convert it to a single-component entity for now

unkempt sedge
#

And tell that to other clients?

#

I am thinking client side prediction with something spice

stiff quiver
unkempt sedge
#

hummm

#

HUMMM

#

it makes sens

pine cape
#

The avian 3d character example is not stable, there's extra rollbacks happening that I can't figure out, but it sounds like running it with a lower replication rate could help provide an intuition for why it's happening

pine cape
floral meteor
#

oh, I think I see the mistake I made now, let me see if I can verify that...

floral meteor
#

yep, okay - I thought add_map_entities was on AppComponentExt, not on AppSerializeExt, so I was under the impression it was exclusive to components as register_resource does not return Self (i.e. the App)

#

it still doesn't work fully, but I suspect that's more an issue on my end and this resource doesn't need to be synchronised

onyx anvil
#

I tried adding the remaining components that should potentially be rolled back as per avian documentation 1, but it still desyncs

GitHub

For things like networking, it can be important to enable rollback for certain physics components and resources to avoid mispredictions. However, it can be unclear which components should be rolled...

#

I couldn't add the Collisions resource though because it doesn't implement Serialize/DeserializeOwned

dreamy silo
onyx anvil
unkempt sedge
#

@pine cape See this boos it is a character customizer with server sided save files, and lobbies that display current players fighting

pine cape
#

Very nice!
Btw can you please not call me 'boss'?

unkempt sedge
#

Sure thingy mr Peri

stiff quiver
#

idk whats worse

onyx anvil
#

Do you have some pointers for how to do input replication with lightyear?

onyx anvil
#

I added some logging here to try to find the cause of the desyncs, but what I'm seeing doesn't necessarily make sense to me

#

also it looks like the server may be overwriting the client's locally measured network characteristics?

dreamy silo
#

I've added a event that gets written and sent in a system in shared.rs, and i have a system that triggers a run on on_event in both client.rs and server.rs. i only get the system running on client.rs and not server.rs. anything I should check? I tried turning off the system in client.rs to see if i get it on server.rs but I can only get it to run on client.rs. the event setup and handling is basically identical to the one i find in lightyear/examples/spaceships. and i tried running the example directly from the repo, and it seems to be working just fine there. not sure what im missing here.

shared.rs:

app.add_event::<BulletHitEvent>();

server.rs:

        app.add_systems(
            FixedUpdate,
            handle_hit_event
                .run_if(on_event::<BulletHitEvent>())
                .after(shared::process_collisions),
        );

client.rs:

        app.add_systems(
            FixedUpdate,
            handle_hit_event
                .run_if(on_event::<BulletHitEvent>())
                .after(process_collisions),
        );
pine cape
pine cape
pine cape
dreamy silo
onyx anvil
pine cape
pine cape
# onyx anvil in this issue it sounds like it should already be mostly supported, but that it ...

I think it's more complicated than that. The tricky part is timeline and input-delay management.

This is my understanding:

  • current design: server replicates state to clients. Clients can do prediction. Clients run RTT/2 in the future compared to the server so that client inputs for tick t arrive at the server on tick t. Input delay can be added to reduce the amount that needs to be predicted.

  • deterministic lockstep with server-relay: all peers (client or server) can only progress if they have received all inputs from all clients. Clients need to run RTT/2 in the past compared to the server, and have RTT input-delay, so that it's guaranteed that all clients will have access to all peers. drawing

  • deterministic lockstep with server-relay and rollbacks: I think what Quantum does is all clients run resim-cap ahead of the server. Every client that has RTT/2 >= resim-cap will add input-delay to compensate. Each client has rollbacks between RTT/2 and RTT/2 + resim-cap
    drawing

So they each require different settings for the input-delay/timeline

Excalidraw

Excalidraw is a virtual collaborative whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them.

pine cape
#

It's not that much work, I guess it would just entail:

  • creating maybe a ReplicationMode enum with 3 variants ServerState, DeterministicLockstep, DeterministicWithRollback
  • adjusting the timeline sync and input-delay logic depending on the variant
  • adjusting the rollback systems (for DeterministicWithRollback, the rollbacks would be triggered based on inputs received, not based on server-state received)
    but it's not entirely trivial
dreamy silo
#

Are entity ids supposed to match on client and server or do we find their relation by looking at client id?

pine cape
#

They don't match, but should be converted automatically by lightyear if you implement MapEntities for your component

unkempt sedge
pine cape
# dreamy silo Are entity ids supposed to match on client and server or do we find their relati...
GitHub

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

onyx anvil
pine cape
#

It's a networking library for unity, but it's 1000$ a month I think

dreamy silo
# pine cape You can look at this example: https://github.com/cBournhonesque/lightyear/blob/m...

https://github.com/cBournhonesque/lightyear/blob/9356eabd048a1ab435ca3fdb82c01738e3ff9ca9/examples/spaceships/src/server.rs#L251

thank you. ive copied and implemented this on my server too the exact same way that’s done here in the example. I now successfully get events to trigger handle_hit_event which I didn’t get previously but now what I get from connection manager via calling client_entity, it’s not matching the entity that is being queried in player_q in the system itself. When I put victim or shooter entity from connection manager’s client_entity into player_q.get_mut, I get no result. Not sure why. maybe what you’re pointing at is what I need to do to get these matching is to put on MapEntities on the player component for it to work. But from the comments it seems only necessary if I know my entity has components with entities inside it. (Children entities) .

GitHub

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

stiff quiver
#

commercial gamedev is so insane sometimes

onyx anvil
#

I think it depends on the # of players

stiff quiver
#

@pine cape saw this randomly btw

stiff quiver
pine cape
stiff quiver
#

why did you clone it anyways if its copy?

pine cape
#

Because it does the same thing, I might have added Copy afterwards

stiff quiver
#

should prob also be minimally faster during compilation because the compiler knows it can just insert a memcpy instead of traversing the clone() call chain

#

oh actually two more things:
Copy-able data can easily be stored on the stack and doesnt need to impl Drop

hexed birch
#

Hi I have a noob question.
I don't understand how to spawn a player sprite for my entity, and have its transform follow the player's position.
I tried spawning the sprite like this

fn player_spawn(
    connection: Res<ClientConnection>,
    mut commands: Commands,
    mut character_query: Query<
        (Entity),
        (Added<PlayerId>, With<Predicted>), // ??? This seems wrong
    >,
    asset_server: Res<AssetServer>,
    mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) {
    for (entity) in &mut character_query {
        // ...
        commands.entity(entity).insert((
            SpriteBundle {
                texture: texture,
                transform: Transform::from_xyz(0., 0., 17.).with_scale(Vec3::splat(2.0)),
                ..default()
            },
        ));
    }

but I think my With<Predicted> is wrong firstly because it only spawns on one client, and then if I do Or<With<Predicted>, With<Confirmed>> it works but the sprite stays still.

pine cape
#

For the object you control, you should spawn the sprite on the Predicted entity (similar to what you did), but for other players' objects, you should spawn the sprite on the Interpolated entity.

(spawning it on the Confirmed entity should work as well, are you sure that the server is correctly replicating your object movement?)

hexed birch
copper basin
#

I converted a basic project to use lightyear. I got it to "work" after a while by looking at the examples but the replication is extremely slow. My fixed timestep is running at 64Hz, im using leafwing and avian3d. A simple debug log confirms that it is not the game state itself that is updating slowly. There is no link conditioner and the problem occurs with both lz4 compression and no compression.
Does anyone have any idea what could be going wrong here? I can send code of course, I dont know what to send so I dont want to spam this channel with that just yet

PS: the server is running as a standalone headless server and has the AssetPlugin, HierarchyPlugin, LogPlugin, MeshPlugin, MinimalPlugins, ScenePlugin, StatesPlugin, TransformPlugin bevy plugins

pine cape
#

I would not use compression at all, i think it might be buggy.
You're only replicating 1 entity and having issues?

Did you try disabling prediction/interpolation and just checking what happens?
Also i think lightyear might have weird behaviours with 0 latency, so I would try adding a little bit of latency via the conditioner (maybe 5 or 10ms)

copper basin
#

Let me try that!

#

hmm you're right, I set both the client and server to use LinkConditionerConfig::average_condition() and its already a lot smoother. still a bit choppy but already better

unkempt sedge
#

Questio is it possible to pick a server replicated resource, grab him in client mutate him and replicate that to server?

pine cape
#

It's choppy because of the absence of interpolation/prediction probably; you're just getting replication updates every replication_interval

copper basin
#

like in the video but a lot less extreme

#

when i dont move for a bit and then move it will move smoothly for a bit and then once again have occasional mini freezes

pine cape
pine cape
copper basin
#

I do have prediction on yes, ill log the amount of rollbacks

unkempt sedge
pine cape
#

No you can directly modify the resource directly on either the client or the server, and the changes will be replicated to the remote. It should just work out of the box; however to detect that the resource was changes I would use the MessageEvent<R> where R is your resource instead of using change detection directly

copper basin
#
2024-10-02T15:49:31.089209Z  INFO bevy diagnostic: packets sent per second              :  124.548113   (avg 90.287223)
2024-10-02T15:49:31.089223Z  INFO bevy diagnostic: KB received per second               : 1368.916624   (avg 1300.508577)
2024-10-02T15:49:31.089226Z  INFO bevy diagnostic: ping.rtt.ms                          :  100.993314ms (avg 94.946543ms)
2024-10-02T15:49:31.089228Z  INFO bevy diagnostic: replication.prediction.rollbacks     : 1382.000000rollbacks (avg 1038.083333rollbacks)
2024-10-02T15:49:31.089232Z  INFO bevy diagnostic: ping.jitter.ms                       :   17.315650ms (avg 5.944809ms)
2024-10-02T15:49:31.089234Z  INFO bevy diagnostic: packets received per second          : 5465.650580   (avg 5192.487123)
2024-10-02T15:49:31.089236Z  INFO bevy diagnostic: ping.pong_received_count             :  214.000000   (avg 188.066667)
2024-10-02T15:49:31.089238Z  INFO bevy diagnostic: replication.prediction.rollback_depth:    7.318379Average rollback depth (avg 7.298254Average rollback depth)
2024-10-02T15:49:31.089240Z  INFO bevy diagnostic: ping.ping_sent_count                 :  217.000000   (avg 189.183333)
2024-10-02T15:49:31.089242Z  INFO bevy diagnostic: replication.prediction.rollback_ticks: 10114.000000ticks resimulated during rollback (avg 7563.433333ticks resimulated during rollback)
2024-10-02T15:49:31.089245Z  INFO bevy diagnostic: KB sent per second   
#

hmm 1382 rollbacks

#

after running for maybe 20 seconds

#

probably something I did wrong then

pine cape
#

Yeah that's pretty bad; I think the avian3d example also has a lot of extra rollbacks though, there is some unsolved issue related to avian3d.
It doesn't happen with the other examples

copper basin
#

yeah I did notice it was slightly choppy on my machine

pine cape
#

(but tbh even with the rollbacks your game should be able to run fine, I don't see why it couldn't handle rolling back for 7 ticks)

copper basin
#

I have a question by the way: when I do

    SharedConfig {
        server_replication_send_interval: Duration::ZERO,
        // the rest..
    }

does that mean every frame as in every time Update is ran or every time FixedUpdate is ran?

#

referring to the /// A duration of 0 means that we send replication updates every frame

pine cape
#

every Update

copper basin
#

ah I see thanks

#

Im running a headless server so there are a lot of updates

pine cape
#

oh yea

#

you don't want to have it set to 0

copper basin
#

Im curious if limiting it will improve things

pine cape
#

your framerate is probably 1000Hz or something

copper basin
#

yeah my fans spin up every time I launch the server

pine cape
#

I would set it to 10Hz to start, it wouldn't be noticeable because of prediction

copper basin
#

thanks for all the help btw!

floral meteor
#

I have a world with hundreds of entities, each with large amounts of data attached to them (~64kB). I noticed that when I updated a few dozen of them at a time (i.e. a few megabytes of updates), I would miss a few updates on the client, so that the client world was left in an inconsistent state.

I assumed that there was a limit to how much Lightyear could send as part of its entity updates, so I switched over to manually sending a message for each entity update (including when a player connects), but now I'm seeing very few of them make it through to the client; of the hundreds sent on join, only a few make it, and very few messages after that on that channel are received (i.e. changing the chunks results in messages being sent on the server, but very few making it to the client)

my questions:

  1. are there limits on how much I can send/receive per tick in Lightyear? are they different between component updates and messages?
  2. should I be expecting an error when doing this? I noticed there was a PR to introduce errors for messages that are too large, but I'm not getting any (I'm unwrapping all of the sends); is this because I'm sending many medium-sized messages, instead of one large-sized message?
  3. how can I debug what's going on here?
  4. should I use some other communication mechanism for this?

for context:

  • the entities are voxel chunks, with a component storing thousands of voxels in a chunk
  • I'm fine with this being totally unusable over the internet right now, I'm just doing local testing to prove out the concept
pine cape
#
  1. There are not limits defined inside lightyear; if you're testing locally I would expect you to be able to handle the load. I would have to think about where the potential bottlenecks are (channel buffers, etc.)

  2. I don't think there would be any errors when doing this; ComponentUpdates is an unreliable channel so it's expected that not all updates make it through. I don't really have any stats per channel available right now unfortunately. There is this StatsManager that tracks overall packet loss though.

  3. I would try to add debug logs mainly around the Channel, MessageManager and Header code to try to understand if the packets get lost, or if there's a problem processing the messages inside the packet.

I had this PR to try to inspect the behavior of sending many large packets: https://github.com/cBournhonesque/lightyear/pull/569/files#diff-6b5453791c93658889b261b77e3b6f0aeef8d8d6f9b94703274c021ef5f1e058R35
(pressing M will send 2000 packets of size 16Mb)

Adding a bandwidth cap should ensure that the game is still playable even while big packets are consuming all the bandwidth, but a lot more testing is needed.

If you're using a reliable channel, I'm surprised that the message is not being received. There is a resend_delay where if we didn't receive an ack for that packet after e.g 1.5*RTT, then we try to send it again. Maybe this leads to the entire channel being saturated as lightyear keeps trying to resend the large messages? Maybe try increasing the resend_delay for your channel

GitHub

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

GitHub

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

copper basin
#

I added a framerate limit to my server and its a lot better now! Even with the server replication send interval at 50ms it was still sending 7000 packets per second but now its more like 80 per second

floral meteor
pine cape
# copper basin I added a framerate limit to my server and its a lot better now! Even with the s...

That sounds like a bug; are you sure? framerate limiting shouldn't really affect anything if you have replication_send_interval set to 50ms. There's a bit of gotcha, but the sent_interval has to be set on both SharedConfig and on ServerConfig.replication, like so: https://github.com/cBournhonesque/lightyear/blob/cb%2Ffix-interpolation-auth/examples/common/src/app.rs#L194-L194
Did you do both?

GitHub

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

copper basin
#

ohh i didnt do that, sorry

#

ty for the info

pine cape
#

Yea it's very misleading right now. The main on is ServerConfig.Replication.send_interval; I still need it on SharedConfig because the client uses it to estimate some interpolation values..

copper basin
#

@pine cape I think I found the fix for my issue #655, I'll submit a PR if it works

#

nevermind that, it seems to be a bug in leafwing

floral meteor
#

I'm a silly goose, the issue was on my end - I forgot that I had other replicated components on the entities, so I had a conflict between the local chunk entities I was spawning and the remote chunk entities arriving facepalm seems to be all working now, thanks for the help!

inner hill
#

replicating Transform rather than Position/Rotation for physics stuff might be a bit smarter, just because it doesn't create conflicts between other stuff you'd like to replicate as well

#

you can't really replicate both without some extra logic to prevent sync loops

unkempt sedge
#

I found out that since their word is "separated", the system alignments became quite simple.

inner hill
#

oh hmm, I think I've been being silly. I should be putting most components on the Predicted entity shouldnt I

#

the non-predicted entity should mainly just be a dummy for updating/seeing current server state

inner hill
#

what would be the best way to add shared components that you'd want to be on both the Predicted/Interp player and the server entity?

#

I can think of maybe a filter for it like:

Query<Entity, (Or<(With<Predicted>, With<Interpolated>, With<HasAuthority>)>)>

But HasAuthority could be on the Confirmed entity as well right?

#

or maybe it should just be Without<Confirmed>? although I worry about that maybe not existing for a couple frames when the entity is spawned

#

this is specifically for components that I don't really want to replicate, they are just extraneous for stuff like rendering (and would be nice on the server as well since having a non-headless mode for the server is nice for debugging things)

pine cape
#

The client Confirmed wouldn't have HasAuthority if you don't have client to server replication

#

In general it's better to use With than Without I think, to handle some edge cases with host server mode

inner hill
#

would this break with host server though?

#

Actually I guess it doesn't matter since you wouldn't have predicted ones since you are the server

pine cape
#

No, on the client acting as server, you would only have 1 entity, which is both Confirmed and Predicted, or Confirmed and Interpolated

#

(I still add Predicted or Interpolated even if you only have 1 entity, so that your existing systems that rely on Prediction/Interpolation still work)

inner hill
#

catapproves gotcha

#

Ty!

summer finch
#

Hey, I'm seeing very strange behaviour with leafwing replicated inputs, it seems that an action's "just_pressed" is getting played back twice.

I have a "shoot" action on left click, which I read with the "actions.just_pressed" method, but with a single click the action is being played back on two different ticks. I have this code in a system that runs every FixedUpdate:

        if actions.just_pressed(&PlayerActions::Fire) {
            println!(
                "Just pressed `Fire` on tick: {:?}/{:?}",
                current_tick_or_rollback, current_tick
            );
        }

and I'm getting the following output (after only a single click):

[CLIENT 1]    |Just pressed `Fire` on tick: Tick(1148)/Tick(1148)
[CLIENT 1]    |Just pressed `Fire` on tick: Tick(1148)/Tick(1148)
[CLIENT 1]    |Just pressed `Fire` on tick: Tick(1149)/Tick(1149)
[SERVER]      |Just pressed `Fire` on tick: Tick(1148)/Tick(1148)

This happens when at times when there's many rollbacks going on (currently due to desynced collisions in Avian, but that's a different problem)

#

Any ideas on how to debug this?

pine cape
#

Is your 'shoot' action also happening in FixedUpdate?
What are your InputConfig settings?

summer finch
#

Is your 'shoot' action also happening in FixedUpdate?
I'm not sure what you mean, leafwing is doing all the input handling. Do I need to explicitly configure anything so that it works in FixedUpdate?

#

This is how I set it up:

#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Copy, Debug, Reflect)]
pub enum PlayerActions {
    Move,
    Aim,
    Fire,
}

impl Actionlike for PlayerActions {
    fn input_control_kind(&self) -> InputControlKind {
        match self {
            PlayerActions::Move => InputControlKind::DualAxis,
            PlayerActions::Aim => InputControlKind::DualAxis,
            PlayerActions::Fire => InputControlKind::Button,
        }
    }
}

impl PlayerActions {
    pub fn default_input_map() -> InputMap<Self> {
        use PlayerActions::*;
        let mut input_map = InputMap::default();

        input_map.insert_dual_axis(Move, KeyboardVirtualDPad::WASD);
        input_map.insert(Fire, MouseButton::Left);

        input_map
    }
}
#

As for the InputConfig, it's the default

#
        InputConfig {
            packet_redundancy: 10,
            send_interval: Duration::default(),
        }
summer finch
unkempt sedge
#

Oh

inner hill
#

how do I add non-leafwing inputs?

#

since those still need to be replayed, can't be as simple as a send_message

dull lion
inner hill
#

yeah I thought about that but it seems kinda silly given that leafwing is optional

#

just not seeing an example with non-leafwing inputs

#

the other problem is this input isnt really a like

#

Axis or something

#

I could use leafwing to transfer it I guess but thats also just jank as hell

summer finch
#

Depends on the kind of input, but in my case I'm adding a "cursor position" input as an dual axis:

pub fn update_cursor_aim(
    mut players: Query<(&Player, &mut ActionState<PlayerActions>), simulated_server_auth!()>,
    q_window: Query<&Window>,
    q_camera: Query<(&Camera, &GlobalTransform)>,
    local_client_id: Res<LocalClientId>,
) {
    // get the camera info and transform
    // assuming there is exactly one main camera entity, so Query::single() is OK
    let Ok((camera, camera_transform)) = q_camera.get_single() else {
        warn!("Cannot get mouse pos, there isn't a single camera!");
        return;
    };

    for (player, mut actions) in &mut players {
        if player.0 != local_client_id.0 {
            continue;
        }

        if let Ok(window) = q_window.get_single() {
            if let Some(world_position) = window
                .cursor_position()
                .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor))
                .map(|ray| ray.origin.truncate())
            {
                // only update the cursor if it's changed
                if actions.axis_pair(&PlayerActions::Aim) != world_position {
                    actions.set_axis_pair(&PlayerActions::Aim, world_position);
                }
            }
        }
    }
}

which runs here:

        app.add_systems(
            PreUpdate,
            inputs::update_cursor_aim
                .run_if(resource_exists::<LocalClientId>)
                .before(InputManagerSystem::ManualControl),
        );

Feels hacky but does the trick

inner hill
#

ah InputPlugin::<Input> seems to be it

dull lion
inner hill
#

Yeah but lightyear will still need a way to send non-button/axis inputs

#

one off messages are good for just saying things like "player wants to buy an item" but for gameplay inputs it doesnt really work

dull lion
#

lightyear does support one off messages but those are not inputs

inner hill
#

Hmm, input plugin doesn't seem to be working here

#

not sure if I've missed something...

#

also wouldn't it be a good idea to have the leafwing input plugins be based on a run condition of is_server/client rather than on initialization?

#

e.g. I have a client that boots up a local server later on

pine cape
#

input::native::InputPlugin is the non-leafwing version of inputs.
I think the book talks a bit about it, + some examples don't use leafwing (for example simple_box if I remember correctly)

unkempt sedge
#

Okay to whon should I attach my other player visuals? To predicted or to replicated? I am guessing me player I should attach to predicted

#

But other player replicated right>?

pine cape
#

For yourself: predicted, for others: interpolated

unkempt sedge
#

thank you mr peri

indigo helm
#

hi, i was wondering if it's possible to replicate a component only to the client(s) that are controlling an entity, or do "controlled only" components need to be added to a child entity that is only replicated to the entities that control it?

pine cape
#

I'm not sure what the second part of your sentence means, but you can set the ReplicationTarget to be the same as the ControlledBy target

indigo helm
#

sorry, i want some components to only be replicated to certain clients, for example the controlling client

#

for example say i have an entity with an item container component, but i don't want it to replicate to clients unless they own it (like a player's inventory) or they're specifically added to a list of clients to replicate to (a player opening a chest to see its contents)

#

the entity has other components on it such as position/etc that i still want replicated at all times so the players know it's there

digital cedar
#

hi, my I ask what is the best way to send player inputs from server to client?

#

context: I am having a server authoritative multiplayer game, I want clients to be able to simulate other clients based on player input, however, I still want the server to have the final say on the player's positions. Also, I want the player to be spawned from the server side as some stuff needs to be generated from the server side and then replicated to the clients

stiff quiver
#

other than that dont think what you are describing is possible at the moment

indigo helm
#

that's what i was gonna do if there weren't a better way. thanks πŸ™‚

digital cedar
#

wanted to know what exactly does this mean?

ERROR lightyear::client::input::leafwing: received input message for unrecognized entity entity=Entity { index: 22, generation: 1 } diffs=[[], [], []] end_tick=Tick(3264)
stiff quiver
digital cedar
# stiff quiver Thats weird, maybe the client wasnt aware that the player had despawned serversi...

don't think so, I did not despawn anything on the server, all I did was spawn an entity on the client with:

pub struct ReplicateInputBundle {
    pub id: PlayerId,
    pub replicate: client::Replicate,
    pub input: InputManagerBundle<PlayerAction>,
    pub prepredicted: PrePredicted,
}

then on the server, I just did this:

pub(crate) fn replicate_inputs(
    mut connection: ResMut<ConnectionManager>,
    mut input_events: EventReader<MessageEvent<InputMessage<PlayerAction>>>,
) {
    for event in input_events.read() {
        let inputs = event.message();
        let client_id = event.context();

        // Optional: do some validation on the inputs to check that there's no cheating

        // rebroadcast the input to other clients
        connection
            .send_message_to_target::<InputChannel, _>(
                inputs,
                NetworkTarget::AllExceptSingle(*client_id),
            )
            .unwrap()
    }
}
stiff quiver
#

Hm no idea then :/

digital cedar
#

does channel direction when registering the components affects the initial replication from client to server?

unkempt sedge
pine cape
# indigo helm sorry, i want some components to only be replicated to certain clients, for exam...

You can use https://docs.rs/lightyear/latest/lightyear/shared/replication/components/struct.OverrideTargetComponent.html to override the replication target for a single component. So you don't need to create a fake child entity!
You can see it being used here for instance

There's probably better ways to design this; see https://github.com/cBournhonesque/lightyear/issues/540
There's nothing right now that would automatically sync that with the list of controlling clients, though

GitHub

bevy_replicon has a disscution including a summary of planned implementation details here projectharmonia/bevy_replicon#304

GitHub

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

pine cape
# digital cedar hi, my I ask what is the best way to send player inputs from server to client?

You can check this example: https://github.com/cBournhonesque/lightyear/tree/main/examples/avian_physics
The inputs are broadcasted to all clients, so that all clients can run prediction on all other clients.
(specifically this system)

This example: https://github.com/cBournhonesque/lightyear/tree/main/examples/spaceships also does this

GitHub

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

GitHub

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

GitHub

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

pine cape
# digital cedar don't think so, I did not despawn anything on the server, all I did was spawn an...

Things are a bit trickier with pre-prediction; you might not be handling it correctly. When the server receives the PrePredicted entity from the client; you need to add Replicate to it, like in this example: here
I would advise you to just spawn the players on the server at first

GitHub

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

digital cedar
# pine cape Things are a bit trickier with pre-prediction; you might not be handling it corr...

Hey thanks! I managed to get the preprediction working! Just curious, if I’m just replicating the input only (the player itself is spawned by the server with prediction turned on), what should be the SyncTarget be? Should it replicate or interpolate at all?

Also in my working example, there are jittering happening on the physics side of things. Not sure what’s wrong, could be because I’m adding correction to linear and angular velocity?

unkempt sedge
#

hmm interestng the system that inserts the interpolated component doesnt seen to run at the same time as the system that add_interpolation in protocols

#

To be expected but man did that screw up my logic

pine cape
pine cape
digital cedar
digital cedar
#

ok increasing the delay ticks to 10 reduces the jittery issue by alot

pine cape
#

Well you shouldn't send your own input back from the server to the client, only other clients'

digital cedar
pine cape
#

Yep :p

unkempt sedge
#

Question let say I have an entity that is predicted, that should contain the following component according to it is protocol.
How can I guarantee my system logic only runs when predicted and player visuals are in my entity?

            .add_prediction(ComponentSyncMode::Once)
            .add_interpolation(ComponentSyncMode::Once);```
#

It seens the systems who do the sync are in different stages

#

Predicted inserted first then PlayerVisuals

wintry dome
#

@pine cape i'm working on deployment stuff with edgegap - current problem is (i think) that the public_ip of the gameserver isn't something you can bind to when the server listens (because AWS maps it using NAT), but it's ok because the server listens on all IPs. however, i don't easily have access to the private IP of the server to add to the internal_addresses bit of the connect token, when issuing the token. so i don't think my connect tokens are valid - server just complains it can't decode packets. Is there a way to tell the server what its public IP is (even if not technically listening on it), so my connect tokens are valid? i've a feeling that might be easier than exfiltrating the private IP of the server and storing it so the matchmaker can include it in connect token calculations.

#

here's my layout. i've nearly got it working end-to-end.. i think it's just my connect tokens that are generated wrong atm. will probably attempt to make a productionized version of the spaceships demo once i have it all sussed.

pine cape
unkempt sedge
pine cape
wintry dome
#
    let token = ConnectToken::build(
        server_addresses,
        state.settings.protocol_id(),
        client_id,
        state.settings.private_key_bytes(),
    )
    .generate()
    .expect("Failed to generate token");

    let token_bytes = token.try_into_bytes().expect("Failed to serialize token");
    let token_base64 = BASE64_STANDARD.encode(token_bytes);

where server_addresses is SocketAddr of the gameserver's public ip, which i tell the client to connect to

#

i was wondering if it's going to fail on server address checks, since the public ip of the server used in the token isn't known to the gameserver bevy app

stiff quiver
#

you could just use a uuid assigned to each server instead

unkempt sedge
#

@pine cape Mr Peri question would you care to review my current project code? Is basically a char customizer with server sided saving mechanics.

#

I found it it would be interesting to add a similar example in lightyear and also since I am newbee I am not sure what i did was right

wintry dome
inner hill
#

hmmm

pine cape
pine cape
silent patrol
#

Hm, I can see this error message on my deployed game server quite often:

[2m2024-10-09T09:47:11.024374Z  WARN quinn_udp: sendmsg error: Os { code: 90, kind: Uncategorized, message: "Message too long" }, Transmit: { destination: 176.36.215.158:52028, src_ip: Some(10.128.0.3), enc: None, len: 1452, segment_size: None }
And a lot of clients fail to establish a connection properly (because of it I believe).

Then clients try to reconnect, and the server spams with errors that a connection token is expired (does it get invalidated after the first connection attempt?)

Not quite sure why this is happening. Is it possible that my configuration is incorrect? Or is it an issue in lightyear or one of its dependencies?

#

Is it possible that my server assumes a higher MTU? I'm using GCP, where it's 1460 by default I believe. Though the error message mentions 1452. Maybe there's some sort of header which adds to the 1452 which then exceeds 1460

indigo helm
#

tokens are indeed only one time use

silent patrol
#

I found that lightyear has a hardcoded constant:
pub(crate) const MTU: usize = 1472

#

I'll try to change the MTU value in my GCP VPC network to 1500. But I guess it would be good for lightyear to have it configurable as well

silent patrol
#

Changing MTU made the error disappear, but that didn't help to fix clients being unable to connect :D
so I tested on Safari first - it failed
Google Chrome - connected
opened another tab in Google Chrome - failed again

I guess I need to get more debug logs to understand what's going on

silent patrol
#

from the client perspective it seems that the connection just times out.. and it only happens with my GCP deployment, not when I'm testing things locally. (But there's a lot that possibly differs: TLS, no latency - for starters)

#

ok, it might be just a browser thing. I can connect from my desktop client to the GCP server no problem

pine cape
#

When you're connecting to the browser, did you first generate new Webtransport certificates?

dreamy silo
#

hey @pine cape , would just like you to know that 0.15 leafwing breaks wasm but has now been fixed in its 0.15.1 version. however 0.15.1 is breaking lightyear. I know you said further up that you're busy lately so I dont wanna stress you on this. not sure if im good enough at this but ill see if I can make a PR for this issue.

dreamy silo
#

im working on a PR rn. hopefully I do it correctly. forking lightyear, then making a new branch called "fix-for-leaf-0.15.1"

#

building it rn to check if compile errors are gone

pine cape
dreamy silo
#

oh right. I think im wilding here.

#

oh I see. im on 0.17.0

#

the fix is found on main

#

ill try changing to main

wintry dome
#

main builds fine, remember to cargo update to pull in latest versions of LWIM if you have your own dep on 0.15

dreamy silo
#

I updated my LWIM version to 0.15.1 in my cargo.toml. I'm getting other errors now tho not related to LWIM πŸ₯² any good way to double check installed crates in my project dir?

#

i know of cargo install --list

#

but thats kinda global isnt it

wintry dome
#

you could always have a look in cargo.lock

dreamy silo
#

oh right

wintry dome
#

if you're on lightyear main and lwim 0.15.1 you should be ok

dreamy silo
#

looks good there

#

i tried cargo clean just now

#

building a new one to see if it clears

#

it now says source git+ instead of registry+. so it should be getting its code from main now

#

alright LWIM support looks good now and is cleared from compile error. but now I see something new.

this happens when i try to run "trunk serve" on lightyear/examples/spaceships

silent patrol
silent patrol
#

it kinda seems that only first Google Chrome client can connect, and all the following attempts to connect from other tabs fail

#

it's definitely not an issue
I'd rather say it's "unlikely" since I can't be 100% sure ofc, but as I said, just after I failed from Safari, I was able to connect from Google Chrome, and then it again failed even on Google Chrome (all the attempts were made within a single 2-3m timeframe)

inner hill
#

it seems like it might make sense to consolidate InputPlugin and LeafwingInputPlugin somewhat

#

leafwing's integration definitely seems much more robust so I'm assuming its just a lot of effort

silent patrol
# silent patrol it kinda seems that only first Google Chrome client can connect, and all the fol...

Lmao, it was connection tokens all along. It turns out, browsers re-use the same TCP connection for different requests! And even for http1?.. O_o (Since my hyper server uses the http1 module)
And my server was increasing client_id counter not on each http request but on each connection... Which is why my different tabs were using the same connection token and their webtransport connection requests were ignored, lol

#

browsers re-use the same TCP connection for different requests
which I mistakenly thought they started doing only for http2

inner hill
#

why not just apply the input buffer action if it exists?

#

and why does it use the active component instead of just the input buffer action

#

I feel like I'm missing something critical here

wintry dome
# inner hill Bit confused by what's going on in the spaceships example here: <https://github....

they way the action state system works is that for remote players, say you've received inputs for ticks 1,2,3 and now it's tick 4 but you didn't receive an input for the remote player for tick 4 yet, the remote player's action state is populated from the input buffer value from the last known input, ie tick 3.

so all that noise is basically to figure out how stale, if at all, the inputs are, so that the game logic can behave differently if it wants to depending on staleness.

#

for example, you might want to keep predicting forward motion, but only for 3 ticks of stale inputs, after that you might either decay the input force or set it to 0

#

the simple approach of just using the action state value would work fine, and be a lot less code, if you don't care about behaving differently depending on how stale the predicted inputs are

#

i can't remember if the spaceships example actually uses the staleness value in its simulation, but i wanted to figure out how to extract that info

#

(ah in fact you can see it only reuses the most recent input up to MAX_STALE_TICKS (6) ticks old)

wintry dome
silent patrol
#

Though it totally makes sense from browser's perspective

pine finch
#

Heyo, am I correct in assuming that ActionState<...> from a server-controlled entity is supposed to be replicated to the clients for prediction?

pine finch
#

It seems that the confirmed entity from the server is getting the ActionState<...> (but with only empty maps?) but it isn't being mapped to the predicted entity below. Not sure if this is intentional, a bug or a skill issue, gunna take a break and mull it over

wintry dome
pine finch
#

hmm, I booted up the avian3d example and it seems to have the same issue from the looks of it

wintry dome
#

ah simple_box doesn't use the leafwing plugin, i meant avian_physics

#

you should get ActionState component on the predicted entity for remote players, on your client

pine finch
#

I'm using a host-server and it doesn't seem to be sending it's own inputs to the client for prediction

#

I assume the host is also supposed to send actionstate?

wintry dome
#

ah i'm unfamiliar with any quirks of host server, but pretty sure remote players' Predicted entities should have an updated ActionState, otherwise your client wouldn't be able to predict their movement (like how avian_physics works)

pine finch
#

Yea, that's my assumption too. My code is basically a re-structured version of the avian3d example, maybe I have to send the host-server inputmessages seperately

wintry dome
#

perhaps someone else who's used host-server can comment. does it work if you run in dedicated server / client mode? assuming you didn't refactor all that away..

#

my game only runs dedicated server style so can't check

pine finch
#

I assume it would since the server is receiving client inputs properly and runs smoothly

#

the avian_3d example w dedicated server seems to do it correctly

pine cape
#

On the host-server, the Predicted and Confirmed entity are the same so the client/server can look at the ActionState and InputBuffer directly

#

You shouldn't need to do anything different

pine finch
#

The screenshot and the behaviour is happening on the client, with a predicted entity that the host-server controls

#

If you boot up the avian_3d example and start one host-server instance and a client, the Confirmed player from the server has an actionstate, but the predicted which points to it does not (at least on my end)

#

And it doesnt seem to be recieving any input messages

#

(Assuming it's even supposed to?)

pine finch
unkempt sedge
#

Can client send message to other clients?

pine cape
#

I haven't written avian_3d so I'm a bit less familiar with it, I would look at avian_physics instead

pine cape
unkempt sedge
#

oh my so usefull i love it

unkempt sedge
pine cape
#

Yes

silent patrol
inner hill
#

Is this normal?

#

getting jitter when this just starts to increase rapidly

#

probably just means my prediction set up is still incorrect since it isn't converging I'm guessing

pine cape
#

It could be normal, if you have 2 players you could have many mispredictions because you don't know the other player's inputs

unkempt sedge
#

Question there seens to be more than one client id in lightyear? Which one should I be utilizing as a premise of my resource, there is one which is a u64 and another which is some sort of enum

wintry dome
#

you might need to pay more attention if you have players connecting from multiple different transports at once

unkempt sedge
#

Ah okay thanks

dreamy silo
#

any advise to which schedule a camera follow system should be in with lightyear + leafwing input manager?

wintry dome
#

if you're not using avian, i think just omit the .after(PhysicsSet::Sync) bit

dreamy silo
#

using avian so this looks promising

#

thanks

dreamy silo
#

could this have any effects i dont want?

pine cape
wintry dome
unkempt sedge
#

If using physics sets in rapier you need to set after syncpropagation honho

dreamy silo
#

Ok so I finally got wasm compiling. But page is white

#

Something up with my target stuff probably

#

The cfg target family wasm thing in my client code is greyed out. Could be related.

dreamy silo
hexed birch
#

Hi I'm getting an error while sending a message

Failed to send message: MessageProtocolError(MissingSerializationFns)

In my protocol I've already registered the failing message
app.register_message::<Message1>(ChannelDirection::Bidirectional);
And I'm not doing anything special sending the message

connection.send_message_to_target::<Channel1, Message1>(
                &mut event.message,
                NetworkTarget::Only(client_ids),
            );
#

When I run with debug, I do see register message Message1 right after add_message with the serializefns

pine cape
#

Hm I can't think of anything obvious. Maybe you're registering Message1 again as something else?

unkempt sedge
#

Perhaps lack of derive

hexed birch
#

Damn, such a simple thing I overlooked... lol
I was using client::connection::ConnectionManager instead of server::...

stiff quiver
# dreamy silo

thats prob just rust analyzer not being configured properly

#

you need to have its target set to wasm too

tulip violet
#

Does anyone have an example of using bevy_streamworks with lightyear using steam transport? Not sure if both need to/can call run_callbacks

stiff quiver
#

@dull lion ^

dull lion
stiff quiver
#

hm does the leafwing integration need both the action state and the input map on the serverside (and if yes does the serverside map need to be the same as the clientside map)? doesnt really make sense for me because users should be able to change their keybinds in the menu or smth, thats like very common practice for most games.

#

@pine cape do you know (if u have time)? :)

#

i suppose the main issue would be differentiating between different input kinds (ie dual axis, button and the like)

#

ig i should just try to reassign a binding on the client and see what happens

pine cape
#

I don't think the server needs to have an InputMap at all, on the client

stiff quiver
pine cape
#

And yes you should be able to just update the InputMap on the client anytime

stiff quiver
#

okay thats good

pine cape
#

No the server just knows the actions (jump), not the input (W)

stiff quiver
#

oh yea makes sense the data would be stored inside the action state

#

okay awesome, but maybe we should at least note that somewhere in the examples

#

cuz they all have shared input maps

unkempt sedge
#

Is there a way to run a system only after the interpolated entities have been fully synced, meaning they all have their given components?

#

Like a sync is complete state?

stiff quiver
#

smth like that

unkempt sedge
#

thank you blobby, you shall go in my default pics for game folder now.

stiff quiver
unkempt sedge
#

no no, legal trouble does not matter into the folder with you.

unkempt sedge
#

Question lets say I have a simple playerposition component and a predicted entity with 4 sub child scenes, how can I guarantee those scenes follow along my player?

pine cape
#

Scenes should be spawned as children of the predicted entity, and transform propagation will make sure that they follow the predicted entity

unkempt sedge
#

hmm

#

I was wondering where in tarnation was the transform being adjusted

unkempt sedge
#

Question should I smooth out my predicted player transform according to time.delta_seconds()?

#
pub(crate) fn shared_movement_behaviour(mut position: Mut<PlayerPosition>, input: &Inputs) {
    const MOVE_SPEED: f32 = 0.1;
    match input {
        Inputs::Direction(direction) => {
            if direction.forward {
                position.z += MOVE_SPEED;
            }
            if direction.down {
                position.z -= MOVE_SPEED;
            }
            if direction.left {
                position.x -= MOVE_SPEED;
            }
            if direction.right {
                position.x += MOVE_SPEED;
            }
        }
        _ => {}
    }
}

/// Ensures transform of all player are adjusted
pub fn update_transform(
    mut player: Query<(&mut Transform, &PlayerPosition), With<PlayerPosition>>,
) {
    for (mut transform, player_position) in player.iter_mut() {
        transform.translation += player_position.0;
        // Add smoothing here? Like time delta and such
    }
}
#

the answer was yes

stiff quiver
unkempt sedge
#

Dont kink shame me this is 3d version of simple box

#

But yes

dreamy silo
#

anyone know what i could be missing with net::ERR_CONNECTION_REFUSED ? ive made sure to give myself a new SSL certificate.

wintry dome
#

and you have to update the CERTIFICATE_DIGEST that the client declared, based on your generated cert

#

The custom certificate requirements are as follows: the certificate MUST be an X.509v3 certificate as defined in [RFC5280], the key used in the Subject Public Key field MUST be one of the allowed public key algorithms, the current time MUST be within the validity period of the certificate as defined in Section 4.1.2.5 of [RFC5280] and the total length of the validity period MUST NOT exceed two weeks. The user agent MAY impose additional implementation-defined requirements on the certificate.

#

I have some code that auto-generates the cert in rust when the server boots, and sends the digest to the client, which I plan to PR to the lightyear examples

#

you need to ensure the CN (or the SAN) of the cert includes the domain youre serving the index.html from too, ie 127.0.0.1

dreamy silo
#

Thanks for all that info, useful to know what’s actually going on with certifications

wintry dome
#

the server outputs the cert digest, so make sure to compile that in as CERTIFICATE_DIGEST so the client knows it, it has to specify that to match the cert on connect

#

actually maybe that's not a const from lightyear itself... but you have to make sure the client knows the right digest when it configures the transport settings somewhere

#

since it has to provide it to the browser when making the webtransport connection

dreamy silo
#

Ok so server gets it directly from the certifications folder files. Client gets it from assets/settings.ron, correct?

wintry dome
#

yeah that sounds correct

#

ensure the settings.ron digest matches what the server outputs

dreamy silo
#

I’ve just been following the readme in spaceships

wintry dome
#

and use chrome, i don't think it works in ff

dreamy silo
dreamy silo
wintry dome
#

openssl x509 -in certificates/cert.pem -noout -text to check the dates are valid and no more than 14 days expiry. should be fine if you used the script provided.

dreamy silo
#

I’m not currently on my computer but will check this tomorrow morning and make sure they’re matching

#

I’ll also check so I don’t have the paths messed up.

#

Thanks for the pointers

#

Will probably get to the bottom of it with this

inner hill
#

@pine cape should always_rollback work?

#

trying to turn it on to debug my issues with rollback not matching/not converging to something

#

I grepped it in the repo and it doesnt seem like its connected to anything?

pine cape
#

I don't think the option does anything right now

inner hill
#

or something to tell me what component is causing a rollback

#

gotcha

pine cape
#

You can turn on debug logs in prediction::rollback

#

To see which component is triggering the rollback

inner hill
#

gotcha, do I need to patch the crate for that?

pine cape
#

No you can just update your log filter in the debug plugin

inner hill
#

o

pine cape
#

Add something like lightyear::client::prediction::rollback=debug

inner hill
#

thank you!

dreamy silo
#

also verified that the cert matches in settings.ron and on the print in server

#

not sure whats going on. need to test more. same error still

#

Server on port 8080, that’s correct right?

#

Clients come in via 5000

wintry dome
#

sounds right, I can check it myself when I'm at my desk in a couple of hours.

wintry dome
#

just tried the spaceships demo in wasm, working here. steps:

  • run sh certificates/generate.sh from top directory
  • cd examples/spaceships
  • cargo run -- server
  • copy the cert digest to the spaceships assets/settings.ron, looks something like "ee:44:35:01:ba:..etc"
  • from spaceships dir, run trunk serve
  • open http://127.0.0.1:8080 in chrome
#

@dreamy silo ^

dreamy silo
#

Will try this again

unkempt sedge
#

Any tips when it comes to camera following predicted entity?

unkempt sedge
#

Ah yes I had the same problem with rapier

#

And I was wondering if this was gonna be a pain again

wintry dome
#

i've been working on some tooling to make it easier to deploy lightyear games to Edgegap, which is a way to automatically spin up game servers in useful locations depending on where the players are. specifically interested in wasm atm for web games, so my system generates a self-signed cert when the server boots and transmits the cert_digest to the client along with the connect token, before it makes the webtransport connection. (connecting from native clients also works fine)

i took the lightyear spaceships demo and extended it slightly to use my bevygap_server_plugin for the server, and bevygap_client_plugin for the client, to make the connect token / auth / matchmaker stuff work.

i've containerized the server, along with an nginx based wasm container, which I deploy behind an existing traefik proxy i use for my blog. github actions does all the building and pushing to docker registries if you commit a v0.1.2 style tag.

i'm hosting a simplistic matchmaker which just lets edgegap decide what server would work best for you, along with autodeploying servers as needed. Takes ~3 seconds to matchmake if it has to boot a new server, and instant enough if there's a server running nearby already.

I still have plenty of tidying up to do, and probably some more error handling, but it's up here if anyone is interested: https://github.com/RJ/bevygap-spaceships (see the link to bevygap repo too).

If you click this with a chrome desktop browser, it's currently online so you can join a game. i'm connected to a server in london, not exactly sure if it will join you to the same as me if you've far away, or spin up another: https://game.metabrew.com/bevygap-spaceships/

dreamy silo
wintry dome
#

i've set the idle timeout pretty low in edgegap's server settings, so it should autoscale back down to zero deployments (ie, zero cost..) if there are no players connected. you do need to keep the matchmaker's httpd and webserver to serve the wasm+index.html online, but that is a low fixed cost with very minimal system requirements. those bits are all containerized too.

dreamy silo
#

It’s a ambitious goal but at least I’ll definitely be learning alot of useful stuff during the process

pine cape
dreamy silo
#

thats my game tho. will actually go try the lightyear demo real quick

#

just to confirm theres something on my end

#

nope. same as on my game. this is the LY spaceships demo. also a new cert.

#

do I need to do any port forwarding on my router maybe

#

or at least unblock it

#

ok nah port forwarding is for something else

pine cape
#

Does it work in native (not in wasm)? Maybe the connectToken is incorrect?

dreamy silo
#

So strange

#

How do I check my connectToken?

wintry dome
#

maybe restart chrome? perhaps it's in a weird state

dreamy silo
#

ill have to figure out how to dig into it. maybe try another system.

wintry dome
#

try in incognito mode? maybe a browser extension getting in the way

wintry dome
dreamy silo
dreamy silo
#

then i have no clue why my connection is getting refused

dreamy silo
#

possible i just go straight to using bevygap

wintry dome
#

i'm not doing anything much different to the demos when it comes to connecting, except auto generating the certificate.

#

add some printouts of the cert digest in the client code, see what it's using

dreamy silo
#

maybe when i run the script it makes for another domain than the one im expecting

wintry dome
#

the script:

# Generates a self-signed certificate valid for 14 days, to use for webtransport
# run this from the root folder
OUT=examples/certificates
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout $OUT/key.pem -out $OUT/cert.pem -days 14 -nodes -subj "/CN=localhost"
#

uses CN (certificate name) = "localhost", so that's what you should use in your browser address bar

dreamy silo
#

ive been visiting 127.0.0.1:8080

#

because thats what trunk says its serving at

#

game does load at that domain tho. just refusing any connections

wintry dome
#

i'm not sure if localhost:8080 would be required, i expect they are treated the same

dreamy silo
#

yeah

#

compiling rn. ill check if localhost gets treated differently

dreamy silo
#

but server is being served at port 8080 with trunk. should that matter?

#

ive made sure the SSL cert is valid and correct on server and client. not sure what more I can try honestly lol

#

ill get to the bottom of it. will try bevygap tho. I could really use the smart fleet feature from edgegap

#

"When you see a message indicating a connection was refused (ERR_CONNECTION_REFUSED) this means that the server actively rejected the connection attempt. This is usually a firewall issue where the firewall rejects the packets."

#

so gotta be something about my setup

#

restarting my mac and updating the OS

#

Sorry for clogging lightyear thread with my isolated issue... I’m definitely having localhost issues which is not related to this anymore. I’ll stop reporting here on my issues and instead find other places to get help if I need it

dreamy silo
#

who am i gaming rn on bevygap spaceships lol

wintry dome
#

me

dreamy silo
#

haha

#

works great

wintry dome
#

i saw the matchmaker logs scroll past so i opened it up

#

where in the world are you btw?

dreamy silo
#

Norway

#

so this is a pretty good location

wintry dome
#

yeah, only 50ms for you

#

~20 for me

dreamy silo
#

been working a few hours tryna figure out my localhost wasm issues. rare im stuck for this long.

#

might try to bake live servers into my game using bevygap

wintry dome
#

have you got any firewall or proxies or antivirus that might be getting in the way

dreamy silo
#

firewall is off. not sure about proxies

#

im on a macos

#

so no antivirus installed

wintry dome
#

bevygap still pretty new.. getting it working locally is worthwhile

#

i'm on mac too

dreamy silo
#

this is what i can find on my proxies settings

#

not sure if any of these should be on

wintry dome
dreamy silo
#

thats pretty cool

wintry dome
dreamy silo
dreamy silo
wintry dome
#

(i mean locally without bevygap, there's a lot of containers to wrangle and general hassle atm, since i'm still making lots of changes)

dreamy silo
#

possible im just gonna continue my game development in native. and solve this issue later

#

or that it by a miracle goes away

wintry dome
#

game dev in native is faster and easier anyway, i only test wasm occasionally

dreamy silo
#

exactly

#

benefits with wasm is that it requires minimal setup for users wanna join and play. letting others join in native requires some work from here.

#

since now im just joining locally via cargo run client -clientid

unkempt sedge
#

If my player should go where his camera is pointing at, how can I also update server as required? Should I just message it the direction of it is camera?

stiff quiver
inner hill
#

honestly Confirmed entities might be better "disabled" in the future once we that ability

#

most of the bugs I run into are me modifying the confirmed entities by accident, but that's never been what I've wanted. just forgot that Or<(With<Predicted>, With<Interpolated>, With<HasAuthority>)> filter on some queries

inner hill
#

man, still just having a lot of issues with mispredictions because of my KCC

inner hill
#

my only guess right now is some sort of system ordering ambiguity because there is seemingly no connection to anything as to when it starts bugging the hell out

inner hill
#

or maybe something with avian not being deterministic somehow?

#

idk I give up for now

frozen carbon
#

So I'm trying to get a very minimal example code running for a HostServer configuration and it seems like I'm missing something. When I try to connect the client in the same instance of the app the server is already running in, I get the following message in the console but it never actually connects to the server: client connecting to server 127.0.0.1:5000 [1/1]
When I try to connect from another instance of the app the client actually does connect.
Here is the code I'm running:

#
fn main() {
    let shared = SharedConfig {
        server_replication_send_interval: Duration::new(0, 0),
        tick:                             TickConfig::new(Duration::from_secs_f64(1. / 64.)),
        mode:                             Mode::HostServer,
    };

    let mut app = App::new();

    app.add_plugins((
        DefaultPlugins,
        EguiPlugin,
        WorldInspectorPlugin::new(),
        ServerPlugins {
            config: ServerConfig {
                shared,
                net: vec![server::NetConfig::Netcode {
                    config: server::NetcodeConfig::default(),
                    io:     server::IoConfig {
                        transport: ServerTransport::UdpSocket(SocketAddr::new(
                            IpAddr::V4(Ipv4Addr::LOCALHOST),
                            5000,
                        )),
                        ..default()
                    },
                }],
                ..default()
            },
        },
        ClientPlugins {
            config: ClientConfig {
                shared,
                net: client::NetConfig::Netcode {
                    auth:   Authentication::Manual {
                        server_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5000),
                        client_id:   1,
                        private_key: Key::default(),
                        protocol_id: 0,
                    },
                    io:     client::IoConfig {
                        transport: ClientTransport::UdpSocket(SocketAddr::new(
                            IpAddr::V4(Ipv4Addr::LOCALHOST),
                            4000,
                        )),
                        ..default()
                    },
                    config: client::NetcodeConfig::default(),
                },
                ..default()
            },
        },
    ));

    app.add_systems(Update, menu);

    app.run();
}
#
/// Test interface for starting server/connecting to server.
fn menu(mut commands: Commands<'_, '_>, mut contexts: EguiContexts<'_, '_>) {
    egui::Window::new("Hello").show(contexts.ctx_mut(), |ui| {
        if ui.button("Start Server").clicked() {
            commands.start_server();
        }

        if ui.button("Connect Client").clicked() {
            commands.connect_client();
        }
    });
}
wintry dome
#

added transfer-encoding chunked streaming support to bevy_http_client, and now my begygap matchmaker streams progress back to the client while looking for a session

unkempt sedge
wintry dome
#

most http get requests just send you back a big lump of text in one go. with transfer-encoding chunked, the server can hold the connection open and send back small messages periodically. so i send a message every second or so with updates, until complete (then the connection closes)

#

similar vibes to a unidirectional websocket connection i suppose

#

it's how we did realtime web stuff before websockets were invented.. πŸ‘΄

unkempt sedge
#

Now I understand

inner hill
#

my run_rollback & update_action_state systems are ambiguous with eachother which sounds a bit bad

stiff quiver
wintry dome
stiff quiver
#

idk who had the idea with the auto generating protocol id but i guess it should be possible to generate it based on the commit hash if you are building your client and server from the same codebase https://crates.io/crates/shadow-rs

wintry dome
#

i recently started using vergen to add build info to server startup logs, quite handy

#

shadow looks simpler to use actually, i may switch

inner hill
#

maybe better, but I was thinking changes that don't change the underlying protocol might be good to allow through

inner hill
stiff quiver
inner hill
#

i think that might be too permissive and has the same problems as the protocol id as it is exists now, have to remember to update it manually

stiff quiver
#

every major update = change in protocol id or smth

stiff quiver
inner hill
#

which part?

#

updating the protocol id?

stiff quiver
#

no updating ur package versioning

#

unless you have like no users or smth

#

idk

#

then prob doesnt matter too much

inner hill
#

I mean as an end user project project version doesnt matter

stiff quiver
#

shrug thats like the location where i keep track of what version im at, which makes sense to let users know

inner hill
#

commit hash is probably a better thing to use for that anyways because you can't mess it up, if its that hash you know the game version based on the git history

#

but you brought up a good point of using commit hash which I'll probably start using haha

inner hill
#

made a jank component to force rollbacks every received server message

#

not sure what's up with it but I get really bad jitters from the rollback

#

or maybe something about input timing to the server...? I'm not sure

#

or maybe interpolation is being wonky, I'll try that first

inner hill
#

I guess I'll also simplify my controller heavily for now to discount that as an issue

wintry dome
#

@inner hill i've noticed some jank similar to what you see sometimes, not sure what is causing it either. happens when latency is low enough that there shouldn't be any rollbacks. haven't got to the bottom of it, moved on to working on some other deployment stuff for now

unkempt sedge
#

aah yes

#

just as i am making a physical character controller

#

i see three people having difficulties

#

lovely

#

LET MAKE IT 4

unkempt sedge
pine cape
#

@inner hill do you have a sample repo to reproduce this?

inner hill
summer finch
#

I didn't have time to debug further yet though

dreamy silo
wintry dome
#

just one, the port that ly listens on. 6000 something in my example, 6420 maybe. on phone atm

#

(UDP)

dreamy silo
#

tried accessing my game after deloyment, but just says unable to connect. probably because i skipped setting up a port

#

this is the host address

wintry dome
#

it will map your port to a different one, so you need to use the API or look at logs to find the external IP and port

dreamy silo
#

oh I found it

#

found the logs. something is not working.

#
2024-10-20T12:55:09.928235Z  WARN server::server_plugin: cert_digest: a0:a6:79:25:3e:d4:5a:d5:e5:d2:e8:72:05:3e:fb:0a:27:a3:49:ca:7f:49:4a:05:54:fd:2b:c0:e1:20:3d:b9
2024-10-20T12:55:09.928259Z  INFO bevygap_server_plugin::plugin: BevygapServerPlugin::build
2024-10-20T12:55:09.928262Z  INFO bevygap_server_plugin::plugin: Reading Arbitrium ENVs
2024-10-20T12:55:09.929491Z  INFO bevygap_server_plugin::plugin: Setting up NATS
2024-10-20T12:55:09.929635Z  INFO bevygap_shared: Setting up NATS, client name: bevygap_server_plugin    
2024-10-20T12:55:09.929656Z  INFO bevygap_shared: NATS_HOST: localhost:4222    
2024-10-20T12:55:09.932738Z ERROR bevygap_server_plugin::plugin: Failed to setup NATS: IO error: Connection refused (os error 111)
thread 'tokio-runtime-worker' panicked at /usr/local/cargo/git/checkouts/bevygap-2cc19569b69827ea/1ebce12/bevygap_server_plugin/src/plugin.rs:164:17:
Failed to setup NATS
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ````
wintry dome
#

yeah you need a working nats install

#

publicly accessible, with valid TLS cert

dreamy silo
#

Don’t wanna bother you too much. I tried your bevygap spaceships. Not sure where I put in my own nat configuration. Since currently it’s looking at 0.0.0.0:4222

wintry dome
#

it's environment variables, NATS_USER etc

#

can set in edgegap app deployment settings

#

I will spend some time documenting bevygap stuff better, but I'm on hol for the next week 😎

dreamy silo
#

Learning as we go. I could potentially just install and run a nats server by putting it in as a job in the yaml file right?

#

I’ll let you enjoy your holiday 🍸

stiff quiver
wintry dome
#

I am half way thru writing the docker compose that runs everything for you. currently entangled with my other sites..

wintry dome
#

so it should scale down to zero cost unless there are players

#

I plan to write a full tutorial to getting it running. happy to answer qs in the meantime but I might be slow to reply this week

unkempt sedge
#

Question when applying lets say a playerposition component. To my transform, should I generally interpolate it? Even tho in he is already being interpolated in sync

#

Right now he just teleports e.e

whole sand
#

My 2c: lightyear will never dictate any of this to you, or even guide you on it

#

Beyond examples

#

It will err on the side of efficiency and so yes you see the more efficient snapping behaviour prevails

#

Also think you may have a small bug where you're adding meshes to the wrong entity but that might be another issue πŸ˜‰

unkempt sedge
#

No it must be Mr Peri would never lie to me

unkempt sedge
#

Anyways do sub-component inherit interpolation and such? For example my velocity, has two sub component linvel and angvel, they also would be interpolated correct? Does nested interpolation exist?

#

answer was yes yay

dreamy silo
#

Almost got a nats server with TLS certs setup to run alongside bevy on edgegap. Self signed. But not sure what CN I need to assign it for it to make a valid cert. hostname?

#

@wintry dome ^

#

Generic like *.pr.edgegap.net

#

That still didn’t work for me, think I need public ip

wintry dome
#

you need the nats server somewhere public that edgegap deployments can connect to. test your connection from your machine with nats-cli first

#

you may have to change the nats connection code in bevygap if you use a self signed cert i'm not sure

dreamy silo
#

So edgegap can’t use a natserver from within the same container?

wintry dome
#

generally the CN of the cert is just the domain name that you type in the browser, or the NATS_HOST

#

i wouldn't run nats on edgegap, it should be somewhere else i think. edgegap just for game servers

dreamy silo
#

Gotcha

wintry dome
dreamy silo
#

Im gonna do the thing and make myself a public natserver

#

Then ask that from edgegap deployment

wintry dome
#

if you have a vps or dedicated server somewhere you can pretty much just run nats docker and expose it, if you get the cert stuff working

dreamy silo
#

Do I need a .env file with your bevygap example?

wintry dome
#

my env file contains NATS_USER, PASS, HOST and the EDGEGAP_API_KEY (or whatever it's called)