#lightyear

1 messages · Page 8 of 1

unkempt sedge
#

Better todo so, and start utilizing native ui

wintry dome
unkempt sedge
#

U mean bevy native?

pine cape
#

Yes, but i haven't found a good replacement yet

#

Are you working on cb/0.18? I already started a lot of the example porting work

#

I wouldn't to duplicate work

wintry dome
#

rebased on CB/0.18 so no duplicate worki hope

pine cape
#

interpolate_quat is not needed anymore?

#

thanks that's helpful

wintry dome
#

yeah the quat impl has it baked in now

pine cape
#

The inputs don't work on avian_3d_character; I'm gonna look at other examples first before coming back to this one

pine cape
#

I've released a new version that is compatible with bevy 0.15! #crates message

wintry dome
#

nice 💪 I'll try and get all the wasm examples hosted soon

unkempt sedge
#

Humm, interesting I made it so my transform is visually interpolatable. And I made a simple system that adds a constant vector to my predicted entity. I keep getting correction rollbacks on it, I wonder why

pine cape
#

Do you have an example repo?

unkempt sedge
#

The logic goes as follows:
First: I register transform in the following manner

 app.register_component::<Transform>(ChannelDirection::ServerToClient)
            .add_prediction(ComponentSyncMode::Full)
            .add_interpolation_fn(TransformLinearInterpolation::lerp)
            .add_correction_fn(TransformLinearInterpolation::lerp);
#

The interpolation being -

pub struct TransformLinearInterpolation;

impl LerpFn<Transform> for TransformLinearInterpolation {
    fn lerp(start: &Transform, other: &Transform, t: f32) -> Transform {
        let translation = start.translation * (1.0 - t) + other.translation * t;
        let rotation = start.rotation.slerp(other.rotation, t);
        let scale = start.scale * (1.0 - t) + other.scale * t;
        let res = Transform {
            translation,
            rotation,
            scale,
        };
        trace!(
            "position lerp: start: {:?} end: {:?} t: {} res: {:?}",
            start,
            other,
            t,
            res
        );
        res
    }
}
#

Second - I made the following really simple movement system in client both of them in fixedupate set

fn move_player(
    mut player_action: Query<
        (&ActionState<PlayerActions>, &mut Transform),
        (With<Predicted>,With<Controlled>),
    >,
) {
    for (player_action, mut transform) in player_action.iter_mut() {
        // You know only act when we actually have something to do
        if !player_action.get_pressed().is_empty() {
            // Make this shared
            if player_action.pressed(&PlayerActions::Forward) {
                transform.translation += Vec3::new(0.0, 0.0, 0.1);
            }
        }
    }
}```
#

And in server

#
fn move_player(mut player_action: Query<(&ActionState<PlayerActions>, &mut Transform)>) {
    for (player_action, mut transform) in player_action.iter_mut() {
        if player_action.pressed(&PlayerActions::Forward) {
            transform.translation += Vec3::new(0.0, 0.0, 0.1);
        }
    }
}```
#

I also made the whole replicate input to other predicted as follows

app.add_systems(PreUpdate, replicate_inputs.after(MainSet::EmitEvents));

pub fn replicate_inputs(
    mut connection: ResMut<ServerConnectionManager>,
    mut input_events: ResMut<Events<MessageEvent<InputMessage<PlayerActions>>>>,
) {
    for mut event in input_events.drain() {
        let client_id = *event.context();

        // Optional: do some validation on the inputs to check that there's no cheating
        // Inputs for a specific tick should be write *once*. Don't let players change old inputs.

        // rebroadcast the input to other clients
        connection
            .send_message_to_target::<InputChannel, _>(
                &mut event.message,
                NetworkTarget::AllExceptSingle(client_id),
            )
            .unwrap()
    }
}```
unkempt sedge
#

I am just gonna do interpolate fuck predict all

unkempt sedge
#

ah manage to make it functional

#

now I only have when i change inputs

pine cape
#

what was the issue?

unkempt sedge
#

Well first issue was with my move_player in that guy, I was only moving controlled player

#

Not all the other predicted players, when they received the input

#

That was one

pine cape
#

ah yes

#

so it works now? if a player keeps moving right, there should be no rollback since the InputBuffer predicts that the same key will be pressed continuously

unkempt sedge
#

Almost

#

When I change inputs he rollbacks

pine cape
#

There should only be rollbacks when inputs change, and they can be attenuated with correction or input delay

#

well yes that's expected, because the client was predicting something different from what actually happened

#

These rollbacks cannot be avoided, but they can be hidden behind animations, etc.

unkempt sedge
#

This guy right

unkempt sedge
pine cape
#

Currently only minimum_input_delay_ticks works, the other settings are broken

#

I just tried visual interpolation in the replication_groups example, seems to work

unkempt sedge
#

Oh just noticed in replication_groups he applies solely in client

#

Yep that was it

#

/// Add the VisualInterpolateStatus component to non-floor entities with
/// component T. Floors don't need to be visually interpolated because we
/// don't expect them to move.
///
/// We query Without<Confirmed> instead of With<Predicted> so that the server's
/// gui will also get some visual interpolation. But we're usually just
/// concerned that the client's Predicted entities get the interpolation
/// treatment.
///
/// Make sure that avian's SyncPlugin is run in PostUpdate in order to
/// incorporate the changes in pos/rot due to visual interpolation. Entities
/// rendered based on transforms will then have transforms based on the visual
/// interpolation.
@pine cape Avian 3d character has a comment that causes constant rollbacks. At least in my example, when I also apply to replication_target everything breaks, in separated server and client mode.

pine cape
#

It should now be possible to have a dynamic input-delay based on the user's RTT

unkempt sedge
wicked tulip
#

has lightyear already adapted to the avian 0.2.0 release?

pine cape
#

yes

unkempt sedge
#

hmm i think the disconnect event is not ocurring

unkempt sedge
# pine cape yes

Question do you have any methods to accelerate lightyear compile time? Not gonna lie in my current setup is takinga few eons

unkempt sedge
#

oh figured it out it seen rust analyzer was resetting my compile times

unkempt sedge
#

oh interesting are we suppose to now manage when a client disconnects and connects? I noticed that now when we press the x or ctrl c in my app terminal server doesnt automatically detect that client has disonnected.

pine cape
#

No it should work, if you try the examples and close a client window

#

The server detects the disconnect

unkempt sedge
#

Yeah that one it does problem are ctrl c in terminal (SIGNINT), the window type I had to make a little function to detect which I think is expected since I am using simple setup instead of lightyear_common

pine cape
#

Did you set the client_timeout_secs in the ClientConfig?

#

By default it's -1 so the connection doesn't time out even if the server doesn't receive any packets

unkempt sedge
swift basin
#

Why is commands.start_server() private?

#

Okay it seems that on latest main its public.

#

My bad I was on old version.

split violet
#

Hey, I have a question regarding how to define my data model for it to be networked properly.: My game is fully server authoritative and clients only send single messages (occasionally) and locally simulate/predict (and add local logic such as UI etc.).
Now I was wandering whether having (replicated) data that contains Entities is a good approach like:

pub struct StructurePath {
    pub waypoints: Vec<Vec3>,
    pub width: f32,
    pub start: Entity,
    pub end: Entity,
}

or

pub struct TroopMovement {
    pub paths: Vec<Entity>,
    // ...
}

I imagine the the entities (-ids) will be replicated by they might not map to the same entity on the client. I'm new to bevy and lightyear, so it would be great to have some idea if there's any best practices when it comes to things like this.

wintry dome
stiff quiver
#

you just need to be aware of the fact that every replicated includes the typeid iirc

#

(can be ignored if you are using delta compression)

#

maybe if we have field level replication someday then this will be unnecessary too

unkempt sedge
#

hmm curious should animation states be in predicted entities?

pine cape
#

I would expect animations to live on the predicted entities, yes

stuck falcon
#

okay, i am intending to do something a bit complicated and need some advice

basically, i want a bevy client to to establish a websocket connection with an actix-web server, which then forwards the actix-ws websocket to a bevy instance running in its own thread (on the server, this is just a lobby) which then puts it into lightyear and thus makes the player join the game

as far as i can tell, most of the work is just implementing the Transport trait for the actix-ws websocket impl, and then somehow (how?) getting that transport into lightyear (as well as have lightyear not listen forn new connections as well because thats not going to happen)

pine cape
#

why can't you just use the websocket transport in lightyear?

unkempt sedge
#

oowee had to refactor my char customizer because he wasnt really functional or understandable

#

Finally got a mmorpg character customizer that is mantainavble

#

400 lines of code for that gotta love bevy animation api

pine cape
fading jasper
#

Any clue why I'm getting this error when trying to connect via websockets?

#

Server:

pub fn start_game_server() {
    let server_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 4433);

    let server_transport = ServerTransport::WebSocketServer { server_addr };

    let config = ServerConfig {
        net: vec![NetConfig::Netcode {
            config: NetcodeConfig::default(),
            io: IoConfig::from_transport(server_transport),
        }],
        ..default()
    };

    App::new()
        .add_plugins((
            MinimalPlugins,
            StatesPlugin,
            ServerPlugins { config },
            ProtocolPlugin,
        ))
        .add_systems(Startup, setup)
        .add_systems(Update, (on_client_connect, on_client_disconnect));
}

fn setup(mut commands: Commands) {
    commands.start_server();
}

Client:

pub struct NetworkPlugin;

impl Plugin for NetworkPlugin {
    fn build(&self, app: &mut App) {
        let server_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 4433);

        let client_transport = ClientTransport::WebSocketClient { server_addr };

        let config = ClientConfig {
            net: NetConfig::Netcode {
                auth: Authentication::Manual {
                    server_addr,
                    client_id: 0,
                    private_key: [0; PRIVATE_KEY_BYTES],
                    protocol_id: 0,
                },
                config: NetcodeConfig::default(),
                io: IoConfig::from_transport(client_transport),
            },
            ..default()
        };

        app.add_plugins(ClientPlugins { config })
            .add_systems(Startup, setup);
    }
}

fn setup(mut commands: Commands) {
    commands.connect_client();
}
pine cape
#

In the client code, try 127.0.0.1 for the server address, not 0.0.0.0

fading jasper
#

same issue

stuck falcon
stuck falcon
stiff quiver
# fading jasper same issue

Weird, it should totally work. Can you double check?

SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4433);

pine cape
#

Maybe try with Chrome

stiff quiver
#

kinda weird

fading jasper
#

I've tried with chrome and firefox

stiff quiver
#

thats so weird

fading jasper
#

I'll try the examples and see if they work. Maybe it's something with my PC

stiff quiver
fading jasper
#

Yes, I ran the simple_box example, using trunk serve --features=client with websocket and I get the same error

#

If I run that example with websocket on native it works

fading jasper
#

wait, the server wasn't running... ok that example does work

#

ok, my game works too. I forgot to call .run() on my server App... lol

pine cape
#

aha cool

pine cape
stiff quiver
pine cape
#

The diff is encoded as 2 i8

#

So there is a loss of precision

#

But basically most of the range of f32 is not needed since the diff between 2 positions should be close to 0

stiff quiver
pine cape
#

This is just in the example

#

The user can choose how they do the diff

#

I'm just not sure how to handle situations where the loss of precision causes the server and client components to diverge

stiff quiver
#

yea thats neat then

stiff quiver
# pine cape I'm just not sure how to handle situations where the loss of precision causes th...

So i think there are 3 scenarios:

  • the client needs accurate precision (ie rotation) -> send diff as regular f32/64
  • the client doesnt need very accurate precision (ie alpha/transparency) -> lose the precision and send as i8/i16
  • the clients needs some precision but not a whole lot (ie translation in some cases) -> send as f16 (which would require a nightly build)

There is also the option of vlq / leb128 but the computational cost probably outweighs the bandwidth savings in most cases.

pine cape
#

I think the encoding itself is fine, the user can just use whatever scheme they need. But let's say the encoding always loses a bit of precision in the same direction (real delta is 300 but encoded delta is 298). After a while the server and client positions will start diverging. How can we make sure that this does not happen?

wintry dome
#

ie reduce the accuracy equally on server just like the network compression will

stiff quiver
stiff quiver
lyric badge
#

when in HostServer mode, do you need to call commands.connect_client() on the server after starting it? is it ok to do it immediately after calling commands.start_server()? if so is there a way to detect that connection? i tried reading from ServerConnectEvent but it doesnt register when it itself connects, only when other clients connect
also checked the ServerConnectionManager but only the other clients are in its connections
am i wrong in thinking it needs to connect to itself?

pine cape
#

Yes, you still need to call connect_client in HostServer mode

#

The Client and Server plugins are still running independently, just in the same world

#

This should emit a ConnectEvent

lyric badge
#

it turns out i messed up the config a bit and i was trying to connect to it through Netcode, it was way easier to just use Local for it 😓
thanks for the answer though!

lucid forum
#

I am trying to run the avian_3d_character example but I am having issues running clients and servers at the same time

cargo run --features=server - creates /debug/avian_3d_character.exe binary and runs it
cargo run --features=client -- -c 1 - tries to overwrite the /debug/avian_3d_character.exe binary, but since the server is running it can not overwrite.

this is giving me this error

error: failed to remove file `C:\dev\lightyear\target\debug\avian_3d_character.exe`

Caused by:
  Access is denied. (os error 5)

how are yall running servers and clients at the same time using the features flag?

fading jasper
#

I had to copy paste the exe, I don't know if there is a better way

#

Might be that it works on linux or something

#

Is there an example for auth that works in wasm?

lucid forum
#

i guess i can just delete the exe manually, i wonder why cargo run isn't able to overwrite in that case

fading jasper
#

because the exe is running

lucid forum
#

seems it is because i'm "unlinking" the running exe (allowed in windows) when I click delete and cargo is trying to actually delete the running exe (not allowed in windows)

#

i looked and there are a couple approaches

  1. setting target directory with cargo run --features=client --target-dir=target/client -- -c 1
  2. adding [[bin]] section in Cargo.toml and setting the binary name and picking the binary using something like cargo run --features=client --bin client -- -c 1
  3. using RUSTFLAGS RUSTFLAGS="--out-dir=target/client" cargo build --features=client

RUSTFLAGS seems to compile from scratch if they get changed so that option kind of sucks. what are other people doing in their projects now?

pine cape
#

Hm i'm on mac and I don't encounter this issue; I can run the binary multiple times without issue

pine cape
wintry dome
#

did we get rid of the way to launch either client or server via a cli param? if that still exists you should just build with --features client,server since then you only have to compile once anyway. in fact i thought the default features included both client and server, in which case building the examples should probably not specify features.. otherwise you are fighting against the building that your code editor is doing when it runs rust-analyzer and you'll get a lot of slow rebuilds because features are changing

#

eg my editor is building with the default features. if i then run on the cli with --features=server that will require a full rebulid, and then when i save a change in the editor, that will require a full rebuild again (because different features enabled)

#

ah, hm. looks like neither client nor server are in the default features for some of the examples. i think they both should be, so that the normal way to try stuff is to not specify features. otherwise you get the fighting-with-your-editor slowdown issue i mentioned, and it's just more of a headache in general. (although essential if you need to do headless/wasm/special builds)

pine cape
#

hm i didn't know that editor builds trigger a full rebuild

#

i thought starting the example with just --features is more elegant than having cli options

#

starting only with the server would require to use --no-default-features then? that's a bit annoying

wintry dome
#

correct.. editor will use defaults, so headless means using no default features plus server. I think we should optimise for the common case of people just running the examples on a laptop, and keep server and client features in defaults. no reason to only build a client or headless example except for deployment of live demos really (and as an example of how to do it..)

#

give it a try? I found it really slow to build non default features interleaved with edit+save

pine cape
#

I use rust rover so I didn't notice this, but i can try

#

Actually when i test the examples i almost exclusive run one headless server in one tab, and then 2 exclusive clients in 2 other tabs

#

I don't people usually try with client-and-server only, because they need to start exclusive-clients to connect to the main server no?

wintry dome
#

yeah i'd test with multiple tabs too, but ideally with the same featureset so the binary is the same. perhaps vscode works differently to rust rover. vscode seems to just use the normal target dir, and builds with default features when it runs rust-analyzer. which is trodden on when you then build without defaults with just server feature for example

pine cape
#

So moving back towards adding cli options to run client, server, gui, etc. ?

stiff quiver
stiff quiver
wintry dome
#

and would eliminate the overwriting .exe problem on windows too, since server and client would all run off same binary

#

(a problem i wasn't aware of.. never built on windows)

pine cape
pine cape
stiff quiver
#

i think if you use sharedarraybuffer you can even avoid the memcopy of the message

wintry dome
wintry dome
stiff quiver
wintry dome
#

can you talk from other windows/tabs/iframes to a webworker? if so it would allow pure web demos of multiple connected clients

stiff quiver
wintry dome
#

neat. i'm a bit behind on "modern" web tech..

#

could make for a nice multi client demo

stiff quiver
stiff quiver
wintry dome
#

hehe yeah i am aware of it but never used in anger myself

lyric badge
#

im trying to send a message, which is ClientToServer only, but i cant seem to get it to recieve...
im in HostServer mode

this is how i registered it:

app.add_channel::<GameChannel>(ChannelSettings {
    mode: ChannelMode::UnorderedReliable(ReliableSettings::default()),
    ..default()
});
app.register_message::<TestMessage>(ChannelDirection::ClientToServer);

this is essentially how im sending the message from the client side:

fn test_message(
  mut client: ResMut<ClientConnectionManager>,
) {
    if let Err(err) = client.send_message::<GameChannel, _>(&mut TestMessage) {
        println!("failed: {:?}", err);
        return
    };

    println!("message sent"); //this prints
}

i recieve the message in the Update schedule like this:

fn recieve_message(
    mut ev_message: EventReader<MessageEvent<TestMessage>>,
) {
    for _ in ev_message.read() {
        println!("message_recieved"); //it never enters here...
    }
}

i was able to send a ServerToClient only message just fine, but i cant seem to get this one to work

pine cape
#

The MessageEvent in your receive_message system is the server's MessageEvent, right?

lyric badge
#

hmm apparently im using the shared one

#

ill try with the server one

#

oh my god that was it

#

thank you

#

very much appreciated

pine cape
#

cool! Yeah it's a bit confusing that it's the same name for both the client and server's MessageEvent

#

but in most cases the client and server code are separate so you just need to do use lightyear::prelude::client::* or lightyear::prelude::server::*

lyric badge
#

well for me it appears as ServerMessageEvent, and ClientMessageEvent, but because i was using the shared one it was just MessageEvent, i think thats what made it go unnoticed by me

#

oh

#

i dont know how to format correctly

pine cape
#

oh i forgot that i also reexport them with different names, that's good

lyric badge
#

yea

#

once again thank you

#

was going crazy over that

molten socket
#

What is the recommended method for adding a singleplayer mode to my voxel game? I know that Minecraft launches an internal server when you join a singleplayer world. I've seen that there's a HostServer mode in lightyear, but is this really recommended? The server and client live in the same application, which can create weird behavior that doesn't happen when joining an external server.

unkempt sedge
#

@pine cape Mr Peri, aid me. When a camera follows a predicted player, what is the ideal system location? I am getting a very annoying flickerance going on

pine cape
# molten socket What is the recommended method for adding a singleplayer mode to my voxel game? ...

HostServer can indeed be tricky to get right and you have to be a bit more careful with your filters. I would just have 2 apps run locally in 2 different threads, similarly to what is done in the examples in Separate mode.
https://github.com/cBournhonesque/lightyear/blob/main/examples/common/src/app.rs#L132

GitHub

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

pine cape
unkempt sedge
#

after it? Godammit

#

I put it before it

unkempt sedge
unkempt sedge
pine cape
#

What do you mean it stutters in a few app initializations? So it stops stuttering after a few seconds?

unkempt sedge
pine cape
#

But the stutter is only at connection time? It doesn't persist afterwards?

unkempt sedge
#

Not it persists

#

but why sometimes it runs okay and sometimes it doesnt

#

GOD this is cancerous

pine cape
#

that means you're missing some system ordering constraint

#

so half the time the systems are ordered in the correct order, and half the time in the incorrect order

unkempt sedge
#

Know of any tool that lets me see the system ordering?

unkempt sedge
pine cape
#

you might be missing some other system order

unkempt sedge
#

System ordering bugs are so annoying when using external crates

#

Oh well guess I will just make my own camera, also your solution wouldnt work with animation systems, as it would look super weird.

pine cape
#

Are your rendering systems also after TransfomPropagate?

unkempt sedge
#

Bevy animation system runs before transform propagate. If you move the camera after such action it looks kinda clunky, source trust me. When debuggin i tried everything

pine cape
#

I don't think it should cause any issues. The camera should be attached to the position of the player after all the frame's updates have been applied (interpolation, transform propagation, animation), etc.

unkempt sedge
pine cape
#

In this video it looks smooth 🙂

lyric badge
#

im using HostServer mode, currently when i close the server i call commands.disconnect_client() then commands.stop_server(), but it seems that the entities with the ControlledEntities componet dont get despawned when i do this, and if i start the server and stop once again it spawns more ControlledEntities, same for the other clients
when a client leaves the server the respective entity with ControlledEntities gets despawned, but never otherwise

am i doing something wrong? or missing something?

unkempt sedge
#

Or perhaps despawning client bound entities

pine cape
lyric badge
lyric badge
# pine cape It's probably a bug. To make sure I understand: - when a non-local client discon...

when a non-local client disconnects, its entities are despawned on the server correctly?
yes
when the local client disconnects, its entities are not despawned on the server
yes, idk if its important but i do want to mention i disconnect the client and stop the server at the same time
when you start/stop the server, it spawns more ControlledEntities for all clients, i.e. the past ControlledEntities don't get despawned?
yes

pine cape
#

Thanks, i'll create an issue for it.
So the issue is mostly happening only for the local-client itself. I'll test it in the case where the disconnection of client/server don't happen at the same time

lyric badge
#

alright, thanks

pine cape
#

@lyric badge I cannot reproduce this. I'm running the simple_box example in host-server mode.
If I disconnect the local client, both the ControlledEntities entity and the player entity get removed

lyric badge
#

let me try it out

lyric badge
#

certainly, the example works correctly, im not sure what is happening with my project then

lyric badge
#

just got back to it, it seems to happen whenever i stop the server, even if i dont disconnect the client on the same frame
if i disconnect the client it despawns the ControlledEntities correctly, so im just gonna delay the server closing for a frame after disconnecting the client, as that seems to work correctly too

pine cape
#

Ok I will try to add a test for stopping the server, thanks

pine cape
stiff quiver
#

Oh no linker issues, the worst kind of compiler error 💀

unkempt sedge
#

@pine cape Lets say you have an npc with pathfinding that path being defined by player would you try to predict it

pine cape
#

No I would interpolate it or do nothing

#

@feral rock do you have more information about the log spam you see in lightyear? I cannot reproduce this in simple box. Which platform are you on?

feral rock
pine cape
#

@feral rock I cannot reproduce this with UDP on simple_box on Mac

#

are you on windows?

feral rock
pine cape
#

are you on windows?

feral rock
#

Yeah

unkempt sedge
#

Question i made an entity pointer like thiss

#[derive(Component, Serialize, Deserialize, Clone, Copy)]
pub struct ServerBaseEntity {
    server_entity: Entity,
}
impl MapEntities for ServerBaseEntity {
    fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
        self.server_entity = entity_mapper.map_entity(self.server_entity);
    }
}```
The next steps to use it is 
        app.add_map_entities::<ServerBaseEntity>();
+
app.register_component etc
+
add the component to the replicated entity correct?
#

And also why does it return me an observer

pine cape
#

Yes,

app.register_component::<ServerBaseEntity>().add_map_entities();

Then everything should be handled automatically, you can treat like any other component

unkempt sedge
#

why does it return me an observer?

#

YES

#

thanks for the confirmation tho

pine cape
#

it doesn't return an observer

feral rock
unkempt sedge
#

@pine cape In egui it is returning the observer type

#

At least in client

#

In server is just an entity

feral rock
pine cape
#

So you're on mac, you're using simple_box example with UDP, and you just close the client window.
You don't get info! from DisconnectEvent and you get a log spam?

pine cape
unkempt sedge
#

ah, HMMMMMM it stills returns me an observer

pine cape
#

actually i was wrong, you first implementation was correct!

#

I guess the entity is not being mapped correctly, so it is assigned to an observer

#

I with the entity-mapping api could return an error when the mapping doesn't work

#

Are you replicating both entities in the same ReplicationGroup?

#

you need to do this to ensure that the mapping works correctly

unkempt sedge
#

perhaps i am implementing this wrong

#

My idea is, store server base entity so later we send base entity to server, when dialogoing with it to ease up the code

pine cape
#

I'm pretty sure what you're doing is this:

  • replicate E1 and E2 from server to client. E2 has ServerBaseEntity(E1) component
  • they are not in the same ReplicationGroup, so E2 can get replicated before E1
  • EntityMapping is applied but it's not applied correctly since E1 doesn't exist on client

Instead what you should do is put them in the same ReplicationGroup to guarantee that they are replicated together

unkempt sedge
#

i see seens about right

unkempt sedge
#

I made a system where a client can have multiple players already spawned when entering the game, I made it so everytime he spawned a child visual and i did a little bit of an animation action. Turns out if you have more than 16 events, in the early stages they die before bevy can consume them cool right?

#

But that doesnt happen with added or changed queries

#

figures

pine cape
#

I'm not sure i understand the full context, but feel free to open an issue in the repo

unkempt sedge
#

it isnt a lightyear issue it is a sort of mine and bevy issue

unkempt sedge
pine cape
#

spawn E1, E2 on server. Add a component C(E1) on E2 on the server. Add replication for E1 and E2 on the same replication group.

Then the entity within C will be mapped correctly on the confirmed, predicted, interpolated entities

unkempt sedge
pine cape
#

Ah you want a component on the client that points to the server entity?

#

You can add C(E2) on E2 then.
But the ReplicationReceiver also has the EntityMap if you want to access the server's mapped entity

unkempt sedge
pine cape
#

Res<ConnectionManager>.replication_receiver.remote_entity_map.get_remote(local_client_entity) would give you the server entity

#

But why do you need access to the server-entity on the client?

#

Everything is mapped automatically so you should pretty much never need that

unkempt sedge
#

I have two subdivision, entities that can be save in my game npc and player

#

Basically i needed to this because my plsyer has, mini followers that only spawn when he spawns. And well cant let him hack it so entity base validation it is

feral rock
#

The spam from the update handler is annoying but not directly related

unkempt sedge
#

Rapier applies all of it physical systems before Transform propagate, I guess that means it wont conflict with visual interpolation

pine cape
pine cape
unkempt sedge
#

So i guess i need to set it is physical schedules before visual intepolation, what is the name of set where visual interpolation logic contained?

pine cape
#

Probably VisualInterpolationSet

unkempt sedge
unkempt sedge
#

it seens rapier applies it is physical simulation directly to global transform, I wonder if that breaks the whole visual interpolation logic

unkempt sedge
#

But he does have a timestep mode that interpolates between positions

#

oh I guess that using him means i dotn actually need to interpolate between my rigid body positions

pine cape
#

No you can still apply visual interpolation to GlobalTransform

feral rock
#

How does one get the IP of a client(_id)?

pine cape
#

from the server? I'm not sure that it's possible in main.
You could get this resource: https://docs.rs/lightyear/latest/lightyear/connection/server/struct.ServerConnections.html
then find the right ServerConnection
from there connection.io().unwrap().local_addr()

unkempt sedge
#

By utilizing a interpolated timestep wouldnt all my troubles just go aways, after all he would just interpolate between his two previous positions, perhaps rollback would break that tho

pine cape
#

It's kind of hard to get info about a specific server right now, I might want to put more things in the ECS as @sonic citrus did to make things more convenient to query; still not entirely sure about pros/cons

pine cape
unkempt sedge
#

In rapier there is the timestep mode interpolated it seens to interpolate it is rigidbodies automatically, do you think it would be necessary to visual interpolate my predicted player in that scenario?

pine cape
#

I'm not sure, I would try both

feral rock
unkempt sedge
swift basin
#

I'm checking event replication that was merged recently. There is no trigger_targets, only global observers for now?

pine cape
#

Ah true, you'd want to trigger an Event on a remote entity target?

swift basin
#

Ideally yes, but there are no expectations, I'm just trying out what has been implemented.

pine cape
#

I created an issue for it

unkempt sedge
#

@pine cape Master Peri, I have been working on interact boxes. That make it possible for my player to interact with my npcs and so on. Should those interaction boxes only be simulated in client, I usually attach them as child of player? Just wondering if my design choice is correct they are physical sensor entities

pine cape
#

Yes it's probably a client-only concern

unkempt sedge
# pine cape Yes it's probably a client-only concern

What do you think of my lerp function for global transform?

/// Struct for handling GlobalTransform interpolation.
pub struct GlobalTransformLinearInterpolation;

impl LerpFn<GlobalTransform> for GlobalTransformLinearInterpolation {
    fn lerp(start: &GlobalTransform, other: &GlobalTransform, t: f32) -> GlobalTransform {
        // Decompose the GlobalTransform into translation, rotation, and scale
        let (start_translation, start_rotation, start_scale) =
            start.to_scale_rotation_translation();
        let (end_translation, end_rotation, end_scale) = other.to_scale_rotation_translation();

        // Interpolate each component
        let translation = start_translation * (1.0 - t) + end_translation * t;
        let rotation = start_rotation.slerp(end_rotation, t);
        let scale = start_scale * (1.0 - t) + end_scale * t;

        let res = GlobalTransform::from(Affine3A::from_scale_rotation_translation(
            scale,
            rotation,
            translation,
        ));

        trace!(
            "global transform lerp: start: {:?} end: {:?} t: {} res: {:?}",
            start,
            other,
            t,
            res
        );

        res
    }
}
pine cape
#

It should work, it looks similar to what I have for transform

unkempt sedge
#

But fixed timestep hz is unsettable in rapier

#

Because of bugg

#

Wonderfull

unkempt sedge
#

I think it probably is something correlated to the adition of collision forces as sensor that doesnt occur

pine cape
#

Are you using pre-prediction? Rollback is broken for pre-predicted entities right now.
Also are all your entities predicted? or only the controlled player?

unkempt sedge
# pine cape Are you using pre-prediction? Rollback is broken for pre-predicted entities righ...

All npcs are just replicated, interesting enough collision works normally with them, although I have rollbacks I can still "push them" now other players are predicted and their inputs resent in that case things are just insane. Perhaps it would be wise to create a rollback state, solely for the physics engines. Similar to it was done here https://github.com/cscorley/bevy_ggrs_rapier_example/blob/main/src/main.rs.

#

Although I am not sure if avian would require the same course of action, curious when you made your avian example did it work flawless? When limit testing avian 3d. I managed to actually make it non rollbackable in a few runs with collsiions occuring, but once one ocurred full desync

unkempt sedge
#

Gonna be honest i dont think i actually need physics, might just make some self made structs that dialogues better with lightyear, and I have control

#

hahahaha, truly i give up on this here I go making a very simple interact_box struct

wicked tulip
pine cape
#

The server keeps receiving acks from the client with the list of packets that the client confirms it has received.
The server keeps sending Deltas from the last acked version of the Component (which we know the client has received)

feral rock
#

Ah, client_server_map is only pub(crate)

pine cape
#

Let me add a function, I think that's a good functionality to have

feral rock
#

Does that map even contain the actual client address?

pine cape
#

nope it contains a map to the NetServer that manages the client

feral rock
#

ah

unkempt sedge
#

Hmm i get a lot of rollbacks when booting up my game, specially when I added physics and player is reconnectiing is that expected?

#

I ought to guess it is as player is allocating to the defined position by server

feral rock
#

beautiful

#

record speed as well

pine cape
unkempt sedge
feral rock
pine cape
#

Recently, because calling this functions by themselves might leave the server in an invalid state. You also need to do some stuff with other resources (ConnectionManager).
Instead these functions are available in commands

#

oh you mean that the Res itself is not public, my bad

#

Just changed it back to pub. In general i'm not satisfied with the ergonomics of having to remember to use ResMut<ConnectionManager> for some things, Res<ServerConnections> for others

feral rock
#

Yeah

#

tbh having to be really careful with the imports because things are named the same on client and server is a bit annoying

pine cape
#

There are also aliases ServerConnectionManager that you can use

feral rock
#

I'm bound on 0.0.0.0 on my server, can't connect over local network hm

pine cape
#

The examples don't work for you?

feral rock
#

Haven't tried them over network yet

stone osprey
#

I was typing out a long question, but I just rubberducked myself lol. While I'm here, thanks for making this crate, I like it!

wicked tulip
#

The avian_3d_character example with the conditioner set as in the example (latency 150, jitter 10, packetloss 0.05) is relatively jittery for me. The conditions seem not ideal, but also not out of the range of normal latency expected in a real world scenario.

Is there something that could be done to make the example feel smoother?

pine cape
#

Is that with only one player?

#

Network conditions shouldn't even matter in this case as everything is predicted

pine cape
#

It might be due to these lines

        // We change SyncPlugin to PostUpdate, because we want the visually
        // interpreted values synced to transform every time, not just when
        // Fixed schedule runs.
        app.add_plugins(PhysicsPlugins::default().build().disable::<SyncPlugin>())
            .add_plugins(SyncPlugin::new(PostUpdate));

VisualInterpolation runs during FixedPostUpdate.
So the flow is:

  • FixedPostUpdate:
    • run physics and update Position
    • store the current tick Position value in VisualInterpolation
  • PostUpdate
    • run PhysicsSync, which syncs the Position value to Transform

(we run PhysicsSync in PostUpdate because Position is updated every frame in PostUpdate thanks to the VisualInterpolation plugin)

  1. maybe there's no guarantee that PhysicsSync runs before TransformPropagate?
  2. maybe there's no guarantee that PhysicsSync runs after VisualInterpolation in PostUpdate?
#

but yeah that's definitely a bug!

#

I have a fix actually, it works well apart from jumps for some reason

unkempt sedge
#

Fun factor, I dont do any of those thing in my 3d game

#

And it is pretty much the same result, everything okay except when dynamic rigidbody collides dynamic rigidbody

wicked tulip
wicked tulip
wicked tulip
#

Just as a side note, if I remove the conditioner, its smooth as butter.

split zealot
#

if i'm running a system on the server that runs in the fixed update schedule, is there a lightyear schedule/set i should be using to make sure it's running in the right order?

pine cape
#

Nope, it should work!

split zealot
#

sweet, ty!

pine cape
#

Does anyone know on the avian_3d example why jumping modifies the y coordinate? I though that z coordinate was up/down

unkempt sedge
#

Y is up and down in bevy, like vertically up and down

wicked tulip
#

I always come back to this as reference:

wicked tulip
pine cape
#

I have several ideas:

  1. this system ordering was maybe missing: https://github.com/cBournhonesque/lightyear/pull/796 Although i still the issue happening sometimes so maybe there's something else
  2. Some component/resource rollback might be missing: https://github.com/Jondolf/avian/issues/478
  3. Some people have mentioned that having Sleeping enabled in Avian could mess with the rollbacks. I'm thinking about this. One solution could be to disable sleeping, another could be to also update a component's ChangeTicks when we rollback it
GitHub

We disable the SyncPlugin in FixedPostUpdate and put it in PostUpdate.
That's because the VisualInterpolation plugin updates Position every frame in PostUpdate, and we want the sync from Po...

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...

unkempt sedge
#

When I messed around with rapier I had to pretty much fully rollback the entire physics world

#

Which mean rollbacking it is ticks too

wicked tulip
#

Disabling sleeping for both cubes seems to not improve it. There still seems so strange missprediction happening

pine cape
#

Yes... not sure what is going on. I tried adding rollback for some avian-specific resources like Collisions;

  1. I see rollback checks failing for AngularVelocity even though AngularVelocity is (0.0, 0.0, 0.0) on both client and server, so maybe something is not written in the history correctly?
  2. The actual Position values are not the same even during rollback, so some logic is not running the same between Client and Server during rollback
  3. If I log the position on the server when the client just connected, I get
2025-01-10T23:55:12.252304Z  INFO avian_3d_character::shared: Player after physics update is_rollback=false tick=Tick(1141) entity=19v1#4294967315 position=Position(Vec3(2.0, 2.9986029, 0.0)) rotatio
n=Rotation(Quat(0.0, 0.0, 0.0, 1.0)) lv=LinearVelocity(Vec3(0.0, -0.15328127, 0.0)) av=AngularVelocity(Vec3(0.0, 0.0, 0.0))

How come i have a negative linear-velocity? Would that mean that the player character is going down through the floor?
This part is so confusing to me

sand sigil
#

e

#

$env:WGPU_BACKEND = "vulkan"; cargo run --features server -- server

unkempt sedge
unkempt sedge
sand sigil
#

Hi, I'm not sure if you guys ever resolved the error for the simple_setup example. when i close the client it spams this error message in the server

#

I'm on windows btw. Cuz I saw you guys mentioned it was platform specific

pine cape
pine cape
pine cape
#

Ok it looks like the constant rollbacks disappear when you disable VisualInterpolation

pine cape
#

Oh actually it's because VisualInterpolation was enabled on the server if I enabled gui

pine cape
#

Other observation: with other blocks removed (only main player remains), there is no rollback. With the blocks, the rollback only happens on the blocks, not on the controllable player

sand sigil
#

I was using the simple setup example. Where do I disable viaualinterpolation in?

#

Oh server

pine cape
#

There is no visual interpolation in simple setup; there is not even an entity replicated 😄

sand sigil
#

Oh were u not talking about the issue I was having?

pine cape
#

Nope i'm talking about the rollback issues in avian 3D

unkempt sedge
feral rock
feral rock
#

Is it possible to access components on the player entity in DisconnectEvent or is that too late? I'm guessing you need to basically maintain your own map of data

unkempt sedge
#

not it is possible, you just need to control it is lifetime so dont make him automatically despawn just because of disconnect

#

What happens to empty rooms in lightyear? Are they automatically deleted?

feral rock
pine cape
#

I try to despawn the client entity only at the end of the frame to let you handle it however you want

feral rock
sand sigil
#

If i wanted to make a p2p network with one peer having authority over the others, should I not even create a server, instead just making an authority client file

#

or should I still use serverconfig and use the server as the authoritative client

pine cape
#

I think you'll still need a server

sand sigil
#

okay thanks

stiff quiver
#

There are still a few problems with that tho but you can try to work around them based on your app requirements

sand sigil
#

i just spawned a camera for the server, not sure if thatll work tho

pine cape
#

Btw i just added a new feature called visualizer where you can view a plot of a bunch of lightyear-related metrics

stiff quiver
pine cape
dreamy silo
#

thats really useful, thanks

feral rock
teal hound
#

Hi, im new to lightyear and have a small question. Im try to use the simple_box example but a bit customized and running in a problem. It looks like i can only accept one client ... so when i connect with the first client, im in succesfull and when i now start my other client its in waiting mode, but when i close the connection from the first, the 2nd client will immediate spawn

#

im running my server and then my 2 clients on the same machine

#

i dont know if i can provide any usefull information 😄

#

2025-01-12T14:58:36.190451Z INFO lightyear::server::networking: Server is started.
2025-01-12T14:58:48.274609Z INFO lightyear::server::connection: New connection from id: Netcode(0)
2025-01-12T14:58:48.475133Z INFO multiplayer_template::server: Create entity 10v1#4294967306 for client Netcode(0)
2025-01-12T14:59:39.061889Z ERROR lightyear::connection::netcode::server: server ignored packet: invalid packet: connect token expired
2025-01-12T14:59:39.182870Z ERROR lightyear::connection::netcode::server: server ignored packet: invalid packet: connect token expired
2025-01-12T14:59:39.303706Z ERROR lightyear::connection::netcode::server: server ignored packet: invalid packet: connect token expired

#

after while when the 2nd client is in connection state i got spammed by the connect token expired

pine cape
teal hound
#

and i didnt found a Event on the client Side

#

the configs and systems are basically all from the simple_box repo, i have none yet for my small project 😄

teal hound
#

ok i reanalysed my code a bit, maybe its only because i tried to merge lightyear with bevy_quickstart, when i remove all the code around it works again ... i dont know, maybe i just need a break 😄

unkempt sedge
#

2025-01-12T22:09:02.037289Z ERROR lightyear::client::input::leafwing: received remote player input message for unrecognized entity
2025-01-12T22:09:02.053712Z ERROR lightyear::client::input::leafwing: received remote player input message for unrecognized entity
2025-01-12T22:09:02.071229Z ERROR lightyear::client::input::leafwing: received input message for unrecognized entity entity=400v85#365072220560 diffs=[[], [], []] end_tick=Tick(514)
So I have the following sceario, my players are predicted entities that are managed by a lobbies resource, as they move from one lobby to another while in game I get that little spawn of leafwing being incapable to replicate message for prediction. Which to be honest is expected as I am yet spawning the player in that room, any ideas on how can i get rid of that?

pine cape
unkempt sedge
#

each day getting close to that godamm rpg state

pine cape
#

nice 🙂

#

I wish we had an editor to be able to iterate faster

unkempt sedge
tropic jackal
#

So I got barebones Client/Server targets running with a WebTransport config. I'm really close getting the client to connect to the server.

Last thing I am trying to do is authenticate the client connection to the server. I'm using Authentication::Manual to get my bearings.

Does the private_key field of Manual need to match the private_key the ServerConfig is using?

tropic jackal
#

@pine cape Thanks - I'm using PrivateKey::load_pemfile() server-side.

Is there a good way to coerce this into Manual.key?

I am currently trying PrivateKey::load_pemfile().secret_der() to get the raw byte array

pine cape
#

What is PrivateKey? I don't think it appears in the repo

tropic jackal
#

It's from the wtransport crate

pine cape
#

load_pemfiles is to load webtransport certificates.
The Manual.private_key is lightyear specific, it's just a 32 bytes that are used to make sure that the client and server are authorized to connect

tropic jackal
#

Ahhh I see. I am confusing the two.

#

Thank you

#

I was trying to load my WebTransport certificate key

pine cape
#

Yep that's what i gathered 🙂

pine cape
#

@wintry dome I switched examples to be back to 'all features enabled', so you shouldn't have the issue with rust analyzer anymore. Also on windows you would only have one binary

wicked tulip
wintry dome
pine cape
wicked tulip
pine cape
#

I think @young prawn has started looking into it: #1124043933886976171 message
You could try to take a look as well to see what could be the source of this.
I had a previous PR that looked at the order of entities during collisions: https://github.com/Jondolf/avian/pull/480
But I think this is the order of iteration of contacts

GitHub

Objective

Fixes Determinism with contacts #406

We noticed an issue where the Collisions were using the order of Entity directly to sort the pair of entities in a contact.
The issue is that for ne...

plain charm
#

Do we need the server side and client side Replicate components to have the same names? If I use them in the same module I need to alias them.

plain charm
#

😮 That's the fastest I've ever seen anyone... anything

unkempt sedge
#

How can I observe, when an message from server was sent to client?

#

Would it be something like Trigger<MessageEvent<message>>

pine cape
#

nope it's not via observers, it's via event readers

#
    for event in reader.read() {
        info!("Received message: {:?}", event.message());
    }
}```
stiff quiver
long marsh
#

are there any examples/documentation/discussion about doing server side lag compensation for interactions between a client’s predicted and interpolated entities? like, the server verifies some physics thing was successful from the client’s pov when the client did the thing

i realize it’s tricky but curious if anyone has taken a stab at it. search here and on gh didn’t turn me up much beyond “yep that’s hard”

pine cape
#

There's no implementation/discussion, just this old open issue: https://github.com/cBournhonesque/lightyear/issues/130

Basically I would expect something like this:

  • add LagCompensation component to entities that can be shot at.
    • We might need some API to specify which components are required to do lag compensation (bounding boxes? colliders? just transform?) but that's not necessary for a proof of concept
  • For all LagCompensation entities, the server maintains a history of where the collider's bounding box was in the last ~300ms. That can be done with this
  • When the server receives the input 'Player C shoots gun at tick T', it somehow determines what the interpolation_tick was for client C when the client's time was at tick T. The interpolation_tick will be something like T - interpolation_delay. Maybe the client can include in the InputMessage what their interpolation tick was at the time of shooting. (and the interpolation_overstep)
  • The server spawns a Bullet at tick T with the information interpolation_delay . When it tries to compute collisions with other objects, it will compare collisions with the other players' bounding boxes with a delay of interpolation_delay, to match what the client saw on their screen. If that happens, it emits a CollisionEvent which is the source of truth used by servers/clients to trigger hit animations, etc.
GitHub

if other players are interpolated in the past, and current player is in the present, we need lag compensation to register hits I believe that lag compensation only applies for very fast projectiles...

GitHub

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

unkempt sedge
pine cape
#

I think in this case event readers might be better because there is a specific schedule where events are read (we first receive all packets, and then buffer all events in EventReaders) so there is no real need for observers

long marsh
pine cape
#

Awesome, let me know if you need any help! And yes the history buffer is used in both prediction/interpolation already

unkempt sedge
unkempt sedge
silent patrol
#

What I might be missing to have components updates coming from a server?

I have the component registered:

app.register_component::<PlayerStats>(ChannelDirection::ServerToClient)
    .add_prediction(ComponentSyncMode::Simple)
    .add_interpolation(ComponentSyncMode::Simple);

I insert the Replicate component on a server (I don't do it on the client end):

entity_commands.insert(Replicate {
    sync: SyncTarget {
        prediction: NetworkTarget::Single(player_id.client_id),
        interpolation: NetworkTarget::AllExceptSingle(player_id.client_id),
    },
    controlled_by: ControlledBy {
        target: NetworkTarget::Single(player_id.client_id),
        lifetime: Lifetime::SessionBased,
    },
    ..default()
});

I don't pre-spawn entities.

I'm sure that component gets modified on the server, but I just don't see it updated on any client... it doesn't matter if I'm inspecting a controlled entity or not, of other player or not, predicted/interpolated/confirmed - I can't see the new values anywhere

The component itself does get created (as in I don't insert it manually, but it actually comes from the server), but it's just never updated

silent patrol
#

I've also tried modifying the values on the client end as well, they were expectedly written to the Predicted entity

but when I changed component sync mode to Full, the updates would be reset to the default value immediately on the next frame (expected, I guess, if there are no updates from the server?)

app.register_component::<PlayerStats>(ChannelDirection::ServerToClient)
            .add_prediction(ComponentSyncMode::Full)
            .add_interpolation(ComponentSyncMode::Full)
            .add_interpolation_fn(|s, o, t| {
                dbg!((s, o, t));
                o.clone()
            });
#

speaking of configs, besides auth and transport configs, this is the only thing that differs from the default one:

pub fn shared_config() -> SharedConfig {
    SharedConfig {
        tick: TickConfig {
            tick_duration: Duration::from_secs_f64(1.0 / 64.0),
        },
        mode: Mode::Separate,
        ..default()
    }
}

it feels like I'm missing something really stupid

pine cape
#

Do you add your protocol after the client/server plugins have been added?

pine cape
#

Do you have a repo link?

silent patrol
pine cape
#

yes

pine cape
#

I would add this log plugin to debug:

            LogPlugin {
                level: Level::INFO,
                filter: "wgpu=error,bevy_render=info,bevy_ecs=warn,bevy_time=warn,lightyear::shared::replication=debug".to_string(),
                ..default()
            }

First I see that you keep receiving constant updates; do you have an entity that keeps being changed on the server?

silent patrol
#

Could those be leafwing inputs?

pine cape
#

No, they were EntityUpdateMessage which are specifically a ComponentUpdate getting replicated

silent patrol
pine cape
#

awesome, thanks

unkempt sedge
#

Question, why does relevance manager requires having pre existing replication targets?

#

Wouldnt that add the overhead of having to se them to a bigger target than they should be

#

Like in my mind, if I add a client id to a room it should just extend it is replication target I might be misunderstanding something

pine cape
#

I think if you are using interest-management, you can just set ReplicationTarget to All, because the entity will only be replicated to clients in the same room anyway

unkempt sedge
#

So I have an interesting pickle I found myself in

/// When joining lobby make the required actions
fn handle_join_event(
    mut join_event: EventReader<MessageEvent<JoinLobby>>,
    mut lobbies: ResMut<Lobbies>,
    mut rooms: ResMut<RoomManager>,
    owned_map: Res<OwnedEntitiesMap>,
) {
    for event in join_event.read() {
        let moving_client: ClientId = event.context;
        let old_lobby_id = &event.message().old_lobby;
        let new_lobby_id = &event.message().lobby;
        info!(
            "Received join lobby {} event from client {}",
            new_lobby_id, moving_client
        );

        lobbies.add_client(moving_client, *new_lobby_id);
        if let Some(new_lobby) = lobbies.list.get(*new_lobby_id) {
            let room_id = RoomId(new_lobby.owner.to_bits());
            if new_lobby.owner.ne(&moving_client) {
                info!("Adding this client to room {}", moving_client);
                let owned_entities = owned_map.map.get(&moving_client).unwrap();
                rooms.add_client(moving_client, room_id);
                rooms.add_entity(owned_entities.player, room_id);
            }
        }

        lobbies.remove_client(moving_client, *old_lobby_id);

        if let Some(old_lobby) = lobbies.list.get(*old_lobby_id) {
            let room_id = RoomId(old_lobby.owner.to_bits());
            let leaving_client_entities = owned_map.map.get(&moving_client).unwrap();
            if old_lobby.owner.ne(&moving_client) {
                rooms.remove_client(moving_client, room_id);
                for entity in leaving_client_entities.iter_entities() {
                    rooms.remove_entity(entity, room_id);
                }
            } else {
                rooms.remove_client(moving_client, room_id);
                // rooms.remove_entity(leaving_client_entities.player, room_id);
                // rooms.remove_entity(leaving_client_entities.npc, room_id);
            }
        }
    }
}```
So here is my code
#

The idea here is quite simple, a client has a bunch of entities that he "owns"( is able to customize and so on). For sake of simplicity, these entities currently are npc and player.
Every client when connecting owns a room, the room id always being his client id to bit.
The scenario I am handling is when a client wants to move to another room. He should still be replicating a few of his owned entities in the room he owns, that entity being NPC. Because if someone enters the room he owns, people can still see his NPC.
Thing is when a client is in two rooms, his entitites still get replicated to him.
How in tarnation I can make it so it doesnt do that?

#

oh I had to remove client from it and only remove player

#

oh my this truly sucked

pine cape
#

You mean that client, player, and npc are in room 1, and then you want to move client and player to room 2, but leave npc to room 1?

#

Maybe I should provide a move_room function?

unkempt sedge
unkempt sedge
#

Outta curiosity why should I reinsert client and remove it if I dont want to replicate to self, when he leaves that room?

pine cape
#

What do you mean by reinsert client and remove it?
The only guarantee is that you replicating to entities to clients that are in the same room as them

unkempt sedge
pine cape
#

So the player entity is not in room 1 and 2, but you still receive updates from that entity? that's a bug

#

When the entity left room 2 it should be despawned on the client

unkempt sedge
unkempt sedge
#

Is like thi Mr peri, let say I remove entity one from room, if I dont remove client id, she still gets replicated to that client if he is self @pine cape

pine cape
#

This is in host-server mode?

unkempt sedge
#

No, separated server

pine cape
unkempt sedge
#

hmmmm

unkempt sedge
unkempt sedge
#

@pine cape Question, if I change a server entity, after what system does it is confirmed state get updated? And therefore it is predicted?

pine cape
#

Entities are replicated after MainSet::Receive in PreUpdate, and the components will be synced to the predicted entity after PredictionSet::SpawnHistory in PreUpdate

unkempt sedge
#

thanks

stone osprey
#

After about 10 seconds after starting the game, the client seems to run the FixedPreUpdate schedule twice every tick. Was this a known issue on main? I'm on e6147de.

2025-01-22T18:33:47.075693Z SERVER  INFO server::pc: Moving PCs 0.1
2025-01-22T18:33:47.162019Z CLIENT  INFO client::pc: Moving PCs 0.1
2025-01-22T18:33:47.176455Z SERVER  INFO server::pc: Moving PCs 0.1
2025-01-22T18:33:47.262925Z CLIENT  INFO client::pc: Moving PCs 0.1
2025-01-22T18:33:47.276766Z SERVER  INFO server::pc: Moving PCs 0.1
2025-01-22T18:33:47.295735Z CLIENT  INFO client::pc: Moving PCs 0.1
2025-01-22T18:33:47.362326Z CLIENT  INFO client::pc: Moving PCs 0.1
2025-01-22T18:33:47.374968Z SERVER  INFO server::pc: Moving PCs 0.1
2025-01-22T18:33:47.395614Z CLIENT  INFO client::pc: Moving PCs 0.1
2025-01-22T18:33:47.462170Z CLIENT  INFO client::pc: Moving PCs 0.1

The 0.1 is the timestep, so it's running multiple times despite the timestep being the same

pine cape
#

The FixedMain schedule might run multiple times in a frame depending on your framerate

stone osprey
#

But it starts to consistently run it twice every time the server runs it

pine cape
#

You're running with 2 apps in the same process?

stone osprey
#

No, they're separate binaries

pine cape
#

Does the server have rendering?

stone osprey
#

No

pine cape
#

Then the frame rate of the client and server will be very different (the server will run the whole schedule more often than the client, and it will comparatively run the FixedUpdate schedule less frequently to compensate)

stone osprey
#

But the issue isn't that the client is running FixedMain comparatively more often than Main, it's running it more often in absolute terms. It starts running it exactly twenty times per second instead of ten times per second

pine cape
#

Are you sure that's the case? You might be getting constant rollbacks, which means the FixedMain schedule is running agin

stone osprey
#

Oh, rollbacks run FixedMain again?

#

So I noticed this issue bc my player suddenly starts moving more quickly after some time passes, after I changed the client to have authority over the player. So my hunch is that it starts rolling back every frame for some reason, and it doesn't handle rollback of entities with client authority correctly

stone osprey
pine cape
#

Can you provide a bit more context on your use-case? Do you spawn directly the entity on the client with authority? Or do you spawn it on the server first and then transfer authority? Does the entity have the 'Predicted' component?

stone osprey
#

I spawn on the client. I think the client_authority example does it the same way

#

*client_replication, my bad

pine cape
#

That example uses PrePrediction, which is slightly different. The entity is spawned on the client in the predicted timeline but the authority is transferred to the server right away

stone osprey
#

Sorry if I'm unclear at times, I'm still wrapping my brain around all the concepts lol

pine cape
#

I see. So your entity is not predicted at all right?

stone osprey
#

Maybe the server predicts it, but I don't think it does

pine cape
#

I think you're right that in the case of a rollback, client-authoritative entities won't be rolledback at all

#

Do you have a repo I could look at?
You can add lightyear::client::prediction=debug to your LogPlugin to check if you have rollbacks

stone osprey
stone osprey
pine cape
#

There is, but I think we should try to clarify the situation a bit more. If there is no server-owned predicted entity there shouldn't be any rollbacks

stone osprey
#

Alright, I invited you

#

The input resolution is in {client, lib, server}::pc::move_pcs

#

I'm using a custom log plugin that's based on Bevy's.

#

If you modify default_filter in there, that'll change the log filtering

pine cape
#

let's chat via DMs

stone osprey
#

Ok 👍

primal cargo
#

Hi, I'm new to this crate. I have a situation where sending a message via NetworkTarget::Single(client_id) is not working correctly. However, when I use NetworkTarget::Only(vec![client_id]), it sends as expected. Should I open an issue? Or is there something I'm missing?

pine cape
#

I have never seen this, please open an issue!

primal cargo
pine cape
#

What was the setup? Were you running in host server mode?

primal cargo
digital cedar
#

I'm still using lightyear for bevy 0.14, is there a bug where i despawn an entity from the server, similar entities with the same archetype would also disappear in the client?

digital cedar
#

ahh nvm, noticed that was not the case, but somehow my blenvy spawned items became non visible..

digital cedar
#

found the issue, it's my issue, ignore me pls 🤣

unkempt sedge
#

Mr Peri, is there a way to validate those send_messages_to_target in server? If so mind telling me how?

pine cape
#

What do you mean by validate?

unkempt sedge
# pine cape What do you mean by validate?

Let say a client sends a message to another foo, he should be able to send that message foo 7 times in one single frame. Is there a way I can check out such factors, like a event that is triggered when that occurs or something.

pine cape
#

On the sender side or the receiving side? There is just the MessageEvent event sender/receiver

unkempt sedge
#

Well it says we first send to server, and them to other clients. Does that mean that server can event read such messages?

pine cape
#

The server always receives all messages, everything packet goes through the server

sonic citrus
#

looking at the code, I see there's the big_messages feature flag which unlocks u16::MAX message fragments. But does this have to be locked behind a feature flag? if you use a varint, could you get up to u64::MAX fragments with minimal protocol overhead?

stiff quiver
lyric badge
#

how come the client's NetConfig::Netcode needs an ip and port for the io and auth?
the auth is for the auth ofc, but does that mean that the actual communication happens with the io address and port?

#

is it fine to have both be the same?

stone osprey
#

Still on e6147de, it looks like incorrectly predicted components aren't being removed or something. I'm getting this log
2025-01-23T18:42:39.659518Z CLIENT ERROR seldom_state::machine: 124v2#8589934716 is in multiple states: lib::ai::Idle and lib::ai::Follow, but the confirmed entity doesn't have Follow even though I'm predicting both components:

    app.register_component::<Idle>(ChannelDirection::ServerToClient)
        .add_prediction(ComponentSyncMode::Simple);
    // ...
    app.register_component::<Follow>(ChannelDirection::ServerToClient)
        .add_prediction(ComponentSyncMode::Full);
#

Oh nvm, I just had to make them both ComponentSyncMode::Full

pine cape
sonic citrus
#

My use case is using a ton of fragments, so I guess it's not that rare

#

not sure why it would use an extra byte though?

#

only if you have more than 128 fragments

pine cape
#

Hm you're right, i'll switch to a varint

lyric badge
unkempt sedge
#

@pine cape Mr Peri, I want to get your opinion on something, I want to make a "window" that show a player in one lobby, what is happening on a different one. What would you recommend?

primal cargo
#

I've registered a message like this:

impl Plugin for MyProtocolPlugin {
    fn build(&self, app: &mut App) {
        app.register_message::<MyMessage>(ChannelDirection::Bidirectional);
    }
}

When I access the MessageEvents on the client using an EventReader:

EventReader<MessageEvent<MyMessage>>

everything works as expected, but when I access it on the server, I get a panic on startup:

could not access system parameter Res<Events<MessageEvent<MyMessage>>>

MyProtocolPlugin is definitely included on both the server and client apps. Is this a bug?

digital cedar
#

make sure that u are using the correct module path lightyear::prelude::client and lightyear::prelude::server

#

@primal cargo

primal cargo
#

That import seems to work fine for the client app

digital cedar
#

oo, i haven't really used that before, maype Peri can explain how that works haha

#

I believe there is also an alias for client/server: ClientMessageEvent and ServerMessageEvent

#

so u can just stick with importing juz the prelude

pine cape
#

It works fine on the client because incidentally the ClientMessageEvent is the same as the shared::MessageEvent

primal cargo
#

Ah that makes sense, I think I'll use ClientMessageEvent and ServerMessageEvent to be more explicit

pine cape
#

actually the ServerMessageEvent is also equal to the shared::MessageEvent in main

#

are you in main or in the latest released version?

pine cape
#

assuming the two lobbies are handled in the same server World

primal cargo
unkempt sedge
#

but them the result of the portal, is going to be on ROOM r not lobby 1 right?

pine cape
#

Or you could add entities from lobby 2 that should be visible through the portal directly in the room for lobby 1

unkempt sedge
#

@pine cape Mr Peri, how can I grab eventreader serverconnectevent via world?

#

let connect_event = world.get_resource::<Events<ServerConnectEvent>>(); is it something like this?

pine cape
#

yes

unkempt sedge
#

Any ideas if I can read it?

pine cape
unkempt sedge
#

yeah you are right

split zealot
pine cape
#

Sure, feel free to contribute a PR as well! I'm not super familiar with the steam code

sand sigil
#

hello, i am using the simple setup example on github and am trying to get multiple clients to connect to the server. i am able to connect a single client with Key::Default(), but whenever i use a key that is not the default key thte server rejects the token saying its "expired" or something. does anyone know a way to fix

pine cape
#

The private key has to match between the server and client, that's for authentication purposes. You shouldn't modify the Key, but instead change the ClientId

sand sigil
#

oh i see thanks

#

so i keep the client key the same for multiple clients

split zealot
# pine cape Sure, feel free to contribute a PR as well! I'm not super familiar with the stea...

https://github.com/cBournhonesque/lightyear/pull/841
The change is super minor, up to you on exactly how the method names and comments should be written.

GitHub

If you can init() on the SteamAPI, it will automatically determine the AppId, otherwise you can call init_app() and provide an explicit AppId.
https://docs.rs/steamworks/latest/steamworks/struct.Cl...

long marsh
#

should an ClientDisconnectEvent fire when a commands.connect_client() fails for some external reason? i have an observer triggered by it that works fine if, say, the connection times out. but error-driven failures (like OS says you aren't allowed to connect on that port, for example) go unreported

do i just poll the state of the resource instead of relying on the events? or am i missing something

edit: ooo. maybe bc the state never actually changes. i see the comment in there about making it synchronous, that would also be fine

pine cape
#

You're saying that in those 2 situations:

  1. client tries to connect, but can't (port already used)
  2. client is already connected, but some io error happens
    we would like to send a ClientDisconnectEvent ?

I think 2) should already be handled; because the on_disconnect function (that runs when we enter the Disconnected state) sends the ClientDisconnectEvent. Let me look into 1)

long marsh
#

that’s right

pine cape
#

I think it's a valid ask, i'm not 100% sure that DisconnectEvent is appropriate here vs something like ConnectionFailure event, but let's just start with that! Fixed with https://github.com/cBournhonesque/lightyear/pull/844

GitHub

We were emitting a DisconnectEvent on the client if we were already connected and a disconnect happened (from io, or from the server, or because the client request it).
However we were not emitting...

long marsh
#

thank you for the quick fix!

long marsh
pine cape
#

Oh you're right

#

The issue is that the DisconnectEvent contains the ConnectionError which is not Clone, so i can't clone it to both trigger it and buffer it in Events...

long marsh
#

i think you already omit the inner error somewhere else. if you did that here, could the user just consume the error from the state of the client connection resource when responding to the trigger?

pine cape
#

I don't really want to expose the fact that the error is stored in the ClientConnectionResource, it's more of an implementation detail

#

I will just do the same thing and omit the reason for now

#

just pushed

long marsh
#

i will not rely on it then 😅 all good for my needs though; appreciate it

long marsh
# pine cape just pushed

i feel so bad pinging you an extra time but you don't have a plain commands reference there, that fn works with world

#

no rush if you are busy, or i can take a stab if you need 🙂

long marsh
pine cape
#

oops sorry i pushed too soon; thanks this looks great 🙂

long marsh
#

hmm, it seems like i have to call disconnect_client() after a disconnect client event before i can try to connect again

behavior is
-> socket already in use error, disconnect fires
-> attempt to reconnect
-> hangs forever

if i manually disconnect the client in response to the event, it works. i guess its missing the state transition in this case?

pine cape
#

You're right!

#

I should have more unit tests for this, but they are fairly tedious to write

long marsh
#
world.resource_mut::<ClientConnection>()
    .disconnect_reason = Some(e);
    
world
    .resource_mut::<NextState<NetworkingState>>()
    .set(NetworkingState::Disconnected);

just centralize the event handling at the state transition, maybe?

pine cape
#

yep that's perfect actually

long marsh
pine cape
#

thanks that's much better

unkempt sedge
#

I think I found a weird bug

unkempt sedge
#

@pine cape Mr Peri, I have dynamic scenes that are being loaded unto server (they are save files of entire "worlds" managed by player). How would I go about registering those entities in the remoteentitymap?

unkempt sedge
#

As they are pre loaded from the dynamic scene file, I think he is not mapping them

pine cape
#

Can you be more specific?
You're loading a serialized scene file into the server? Was the file generated by the client?
If you're just loading a scene into the server and then replicating it to other clients, everything should work fine since those entities are just treated as if they were created originally on the server

violet topaz
#

I'm having a issue, where every time, that i close my client, i'm receiving the following error

2025-01-27T01:28:43.518412Z  INFO lightyear::server::connection: New connection from id: Netcode(0)
2025-01-27T01:28:43.719177Z  INFO server: Client 0 connected
2025-01-27T01:28:49.138120Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))

and this is never called

fn handle_disconnect_event(trigger: Trigger<DisconnectEvent>, client_ids: Res<ClientIds>) {
   if let Netcode(client_id) = trigger.event().client_id {
       client_ids.0.write().unwrap().remove(&client_id);
       info!("Client {} disconnected", client_id);
   }
}

Does anyone have any hint of what can be happening? Thanks!

long marsh
#

i think the reverse problem exists on the client too, if you alt+f4 the server

violet topaz
#

Oh, so in fact it's a bug, i thought that for some reason i was missing another observer for network errors :/

unkempt sedge
#

I will make a mre give me a second

unkempt sedge
pine cape
# violet topaz I'm having a issue, where every time, that i close my client, i'm receiving the ...

I just tested it in simple_setup but I actually don't get any error. Which transport are you using?
Do you receive the error many times or once?

I think the solution would be to listen for the actual error type in
https://github.com/cBournhonesque/lightyear/blob/bc749d6185ce8751cbf97ec52d8c47de547fcd80/lightyear/src/server/networking.rs#L143-L145
And switch to Disconnected state if the error is not recoverable

GitHub

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

pine cape
unkempt sedge
#

oops my bad

#

I made a quick morning commit before leaving broke the sample hold up

pine cape
#

ah i see the error now. This? 2025-01-27T16:19:20.711877Z INFO lightyear::shared::replication::receive: update for entity that doesn't exist? remote_entity=16v1#4294967312

unkempt sedge
#

The general idea of it is that, this dynamically saved entity, should be replicated to client. That doesnt occur when, we use rooms, but it does with no interest management. Weirdly in the MRE no, error is given

scene_world
  .spawn(ComponentA(2))
  .insert(CarrierId(client_id))
  .insert(Name::new("Replicated entity"));```
pine cape
#

It's independent from the Scene thing; it's because you add the entity/client to the room before the client exists

#

So the NetworkRelevanceCache considers that the entity has already been replicated to the client

#

it's a bit niche; normally you shouldn't add clients to the room before they are connected as you shouldn't have guarantees on what their client id is

unkempt sedge
#

As always Mr Peri you are right yay

pine cape
#

it worked? nice

unkempt sedge
#

Yes it worked when he is guaranteed connected

fn add_replicate(
    query: Query<(Entity, &CarrierId), With<ComponentA>>,
    mut commands: Commands,
    mut rooms: ResMut<RoomManager>,
    mut lobby_yes_or_no: Local<bool>,
    mut event_reader: EventReader<ServerConnectEvent>
) {
    for event in event_reader.read(){
        for (entity, carrier_id) in query.iter() {
            let client_id = carrier_id.0;
            *lobby_yes_or_no = true;
    
             if *lobby_yes_or_no {
                let room_id = RoomId(client_id.to_bits());
                let replicate = Replicate {
                    target: ReplicationTarget {
                        target: NetworkTarget::All,
                    },
                    relevance_mode: NetworkRelevanceMode::InterestManagement,
                    ..default()
                };
                rooms.add_client(client_id, room_id);
                rooms.add_entity(entity, room_id);
                info!(
                    "Started to replicate entity {} with component A in lobby",
                    entity
                );
                commands.entity(entity).insert(replicate);
            } else {
                let replicate = Replicate {
                    target: ReplicationTarget {
                        target: NetworkTarget::All,
                    },
                    ..default()
                };
                info!("Started to replicate entity {} with component A", entity);
                commands.entity(entity).insert(replicate);
            };
        }   
    }
}
violet topaz
# pine cape I just tested it in simple_setup but I actually don't get any error. Which trans...

**Q: **I just tested it in simple_setup but I actually don't get any error. Which transport are you using?
A: Netcode

**Q: **Do you receive the error many times or once?
**A: **It keeps logging in a infinite loop, even after the timeout.

2025-01-27T16:37:25.422420Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))
2025-01-27T16:37:25.423125Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))
2025-01-27T16:37:25.522661Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))
2025-01-27T16:37:25.523377Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))
2025-01-27T16:37:25.623059Z ERROR lightyear::server::networking: Error updating netcode server: Netcode(Transport(Io(Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." })))
Process finished with exit code 0

})))

The problem might be me, ive just started with rust, here's a branch with it.
https://github.com/arthurpessoa/bevy_multiplayer/tree/LIGHTYEAR_ISSUE

GitHub

Contribute to arthurpessoa/bevy_multiplayer development by creating an account on GitHub.

unkempt sedge
#

@violet topaz are you brazilian friend?

pine cape
long marsh
#

Windows/Udp for me, i didn't try any other combos. think imma just fix it tho

violet topaz
violet topaz
pine cape
long marsh
# pine cape what solution do you have in mind

well i was going to do what you had said, except now that i've looked into it...that Result being thrown away there represents a pass/fail for the entire try_update, all clients. there's no mechanism from right there to identify which individual client had a failure that bubbled. that error is a ConnectionError which doesn't contain anything to id a client, nor could it handle multiple client failures distinctly atm

i am guessing it should be handled somewhere in ./connection/netcode/server ? haven't really sussed out where yet

long marsh
#

yeah

https://github.com/cBournhonesque/lightyear/blob/bc749d6185ce8751cbf97ec52d8c47de547fcd80/lightyear/src/connection/netcode/server.rs#L806

this receiver.recv() is what is failing. but as i sought the root cause here, i found a bunch of bubbled (client specific?) errors that could fail in exactly the same way. did you want to handle them all in-place, bubble them up in a collection somehow, or something else?

i am guessing this is exclusive to netcode / doesn't happen on steam, so handling it outside the netserver abstraction feels wrong

#

like either the error is recoverable and we ignore it / wait for timeout, or it's not and we disconnect the client. returning a big pile of ConnectionError from try_update might just be useless

pine cape
#

I assume it might also happen on steam; if there's some kind of error the try_update() in recv_packets() will start failing and showing errors

pine cape
long marsh
pine cape
long marsh
pine cape
#

Ah, I was thinking that the ClientId would be added in the ConnectionError, but that sounds a bit unwieldy..

#

Maybe the best is to disconnect the client directly inside the netcode server code, as you suggested

violet topaz
pine cape
#

I'm also not a fan of the current design where the receive() function returns early on any error, because it seems to be that some packets can get lost that way). Ideally errors would get buffered somewhere else and a separate thread could take care of them, so that the try_update() function can finish even if one message causes an error

long marsh
#

the naive answer is to just pass around a vec of (ClientId, ConnectionError) then.

all the io is single threaded / effectively synchronous? so i can just…do that? seems like the path of least resistance for now, if that’s an acceptable solve for you

can follow-up with whatever else that wants to consume those errors easily too then

pine cape
#

Stored in the ServerConnection? That should work.
Although 'm not sure if it's easy to determine if the error is recoverable

long marsh
#

gonna argue that nobody is seeing much of these error paths right now. i figured for anything that wasn’t obviously critical, it could just be a warning, and we can refine the criticality of them through the wisdom of experience…

edit: and this only refers to if we call disconnect or not; i think we still keep every error around for inspection

plain charm
#

What’s the most straightforward way to have the terminal spit out every single thing lightyear is doing? Super verbose mode. All the stats. I got some config to do and I’m not sure if I’m doing it correct

pine cape
#

2 ways:

  • use the visualizer feature (in the main branch) and you will get extra egui dashboards showing detailed stats about lightyear internals
  • update the log plugin with lightyear=trace (or you can be more specific: lightyear::client=trace, etc.)
unkempt sedge
pine cape
unkempt sedge
#

which means I would require some sort of username login system?

#

To correctly map out who that player entity belongs to correct?\

#

well

#

ought to be fun to do it but still I will have to do a lot of remaping

long marsh
pine cape
#

looks like it! Also it seems weird to me on re-read that I don't bubble most of the errors, for example on ConnectTokenPrivate::read_from at the start

pine cape
#

I'm trying to move lightyear to a multicrate architecture like bevy; it would probably help making things more module/self-contained but i'm not sure about the consequences in terms of compilation time

#

I'll probably push it off to 0.20

long marsh
#

oh interesting! the abstractions around netcode did feel kinda funky (or at least, a little fuzzy on a straight read) to me. nothing a little iterative improvement can't fix

sand sigil
#

to make sure im understanding everything correctly, what I need to do to replicate every entity from the server to the clients are when the server detects a new client connection through the ClientConnect event, it spawns a new client entity where the client entity has the replicate bundle. then am i supposed to add the visual components in the client plugin?

#

this is the function i made to spawn in players on the client side. ```
fn display_client(
mut materials: ResMut<Assets<ColorMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
mut commands: Commands,
player_query: Query<(Entity, &PlayerBundle)>,
) {
for (entity, player) in player_query.iter() {
println!("Spawn worked");
commands.entity(entity).insert((
Mesh2d(meshes.add(Rectangle::new(50.0, 50.0))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(Color::BLACK))),
Transform::from_xyz(player.position.x, player.position.y, 1.0),
));
}
}

pine cape
#

Yes, I would use an observer with something like Trigger<OnAdd, PlayerId> to react the replication of a new player entity on the client side.
You can also use an additional query Query<(), With<Replicated>> to confirm that the entity was spawned via replication

long marsh
#

@pine cape There are some cursed things about the error handling. Here is the worst one so far!

https://github.com/Piefayth/lightyear/pull/1/files#diff-45f9b72e7df00f04677e09f1365496f53b12004a5bf6df5275ca7e82ac34d7a2R809-R811

To disconnect someone, you either need the ClientId or the SocketAddr, yes? Well, in the specific case where we are getting the problem I was originally trying to solve - the failure of receiver.recv() that we discussed - I don't see any way to get the address in the sad path. The transport implementations are not obligated to inform the caller of .recv() what connection failed in the failure case. So how can the caller disconnect that client?

pine cape
#

Released version 0.19! #crates message

long marsh
# pine cape Hm it seems similar to what i found there: https://github.com/cBournhonesque/lig...

It doesn't make sense for recv to be the problem, though.

2025-01-27T00:04:26.601595Z ERROR lightyear::client::networking: Error updating netcode: netcode error: An existing connection was forcibly closed by the remote host. (os error 10054)

UDP is stateless! What existing connection!? Doesn't pass the smell test.

So I did a little research. What can happen is, the server tries to send a packet, but the client closed the connection on their end. Well Windows doesn't report such errors on send, they get queued to receive.

By updating the UDP send impl to peek the socket on Windows...

impl PacketSender for UdpSocketBuffer {
    fn send(&mut self, payload: &[u8], address: &SocketAddr) -> Result<()> {
        let socket = self.socket.as_ref().lock().unwrap();
        
        #[cfg(target_os = "windows")]
        {
            let mut peek_buf = [0u8; 1];
            match socket.peek_from(&mut peek_buf) {
                Ok(_) => (),
                Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => (),
                Err(e) => return Err(e.into()),
            }
        }


        socket.send_to(payload, address)?;
        Ok(())
    }
}

We can now access the error in send_packets with a client_id

2025-01-28T02:28:23.663182Z ERROR send_packets: lightyear::server::networking: Error sending packets: netcode error: client_id Netcode(11040662130883646883) client specific transport error An existing connection was forcibly closed by the remote host. (os error 10054)
pine cape
#

Awesome, that's a great find!

#

you're a pretty good investigator 🙂

#

What are you building with bevy btw?

long marsh
#

i love debugging! i am an older (?) professional in non-games

i am just prototyping lately. i wrote some rollback netcode with unity DOTS a long time ago. was gonna port it over to bevy, but seems easier to contribute to something that exists already. happy to skip a lot of the connection plumbing and byte-muckery. i'm a high level kinda guy

#

think i'm gonna ultimately try to slap together an arpg/moba feeling character, network it, and see if i can make something feel good

pine cape
#

Sounds good! trying to network a game is probably the best way to figure out what's missing in lightyear

#

For example i haven't tried handling animations/sounds with rollback

hexed ruin
#

Have you tested physics with rollbacks as well?

pine cape
#

Yes the avian_3d and avian_physics examples work with rollback; they mostly work but there's still a couple causes of non-determinism in avian that cause rollbacks

hexed ruin
#

Are they pretty bad?

pine cape
#

They''re not noticeable, it's just annoying that the client is rolling back constantly for no reason

hexed ruin
#

Well that’s good. Does make severely impact cross platform determinism?

pine cape
#

I haven't tested it cross-platform but avian has an enhanced-determinism feature that should guarantee cross-platform determinism.
I think the issue here is that the server and client world have a different number of entities which modifies the order that avian iterates through entities for collision-handling

long marsh
#

@pine cape for your consideration https://github.com/cBournhonesque/lightyear/pull/854

improves error handling for just netcode server and fixes the windows error spam on client disconnect. left some comments. lmk what you think of doing it like this / if you see something to simplify

sand sigil
#

The server is properly recieving the clients when they are connected, so i'm not sure what the issue is. this is the function for servers recieving oncoming client connections


pub fn handle_connections(
    mut connections: EventReader<ConnectEvent>,
    mut map: ResMut<ClientEntityMap>,
    mut commands: Commands,
) {
    let spawn_range = (1000.0, 1000.0);
    let mut rng = rand::thread_rng();
    let rand_pos_x = rng.gen_range(-(spawn_range.0 / 2.0)..(spawn_range.0 / 2.0));
    let rand_pos_y = rng.gen_range(-(spawn_range.1 / 2.0)..(spawn_range.1 / 2.0));
    for connection in connections.read() {
        let client_id = connection.client_id;
        let replicate = Replicate::default();
        let entity = commands.spawn((
            PlayerBundle {
                client: client_id,
                position: Vec2::new(rand_pos_x, rand_pos_y),
            },
            replicate,
        ));

        // Add a mapping from client id to entity id
        map.map_id.insert(client_id, entity.id());
    }
}```
#

as you can see, the server is recieving clients when they connect but for some reason the clients arent recieving the replication bundle from the server. any thoughts? Thanks

pine cape
#
  • Did you use the bevy_inspector_egui to check that the entity was replicated on the client?
  • Does it work with just Trigger<OnAdd, PlayerId>? What does your observer look like?
  • is PlayerId in your protocol?
sand sigil
#

PlayerID is not in my protocol, I was using PlayerBundle which is just a custom component instead

#

Do you mean ClientId?

pine cape
#

I just assumed PlayerBundle was a bundle, not a component. If you send me a link to your project I can take a look

sand sigil
#

ill upload it to github. thanks for taking the time

sand sigil
pine cape
#

You are missing the PlayerBundle in your protocol

sand sigil
#

ohh

pine cape
#

Might need something like

        app.register_component::<PlayerBundle>(ChannelDirection::ServerToClient)
            .add_prediction(ComponentSyncMode::Once)
            .add_interpolation(ComponentSyncMode::Once);
sand sigil
#

do i put this in shared?

pine cape
#

yes

sand sigil
#

thanks for the help, ill try that

sand sigil
long marsh
sand sigil
#

right now i'm just using a local port to host the server, but in the future if i wanted to make an actual game would it just be a matter of switching the server address to the address of a server i'm hosting or would there be any other configuration that i would need to do?

pub const SERVER_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5000);

pine cape
#

Yes you would just need to change the SERVER_ADDR

sand sigil
#

thanks

long marsh
#

what's the idiomatic way to exclude entities from replicating a replicated component? say i want to replicate Transform once for all my level geometry, but i want full sync for my players' Transform.

i thought i'd just put them in a different replication group, but ReplicationGroup doesn't seem to expose enough for me to selectively control the updates like that.

#

nothing else on Replicate looked helpful. i could make a SyncTransform component and just copy it back and forth to Transform where i want, but that's kinda gross

pine cape
long marsh
#

yep works 👍

plain charm
#

Alright, I'm switching to having two hostserver mode apps running. One runs start_server(), the other connect.client(), but cannot connect. They both have identical auth, addr (LOCALHOST), shared, etc.. What am I missing in the process?

#

Same machine, two different apps. Connection attempt times out for client

#

Do I need the client to reconfigure to a Separate mode to connect to the other hostserver app?

pine cape
#

It should be possible to run them both in HostServer. Actually in the lobby example all clients start in HostServer mode

plain charm
#

Oh, it looks like there is a client config. The host changes their client's NetConfig to Local, and everyone else goes to NetCode

#

I see. HostServer must config client to Local, start_server(), then connect themselves with connect_client(). Incoming "remote" clients need to config to NetCode to join

pine cape
#

Yep exactly, it might be possible to simplify this

pine cape
#

I'll probably going to get rid of replication events such as ComponentInsert, ComponentUpdate, etc.

#

they don't seem that useful, and are redundant with bevy's own Changed<>, Added<> filters

plain charm
stiff quiver
#

Wow lightyear progressing at lightspeed atm, wish i had enough time on my hands to check out all the new stuff

#

congratz on the new releases

pine cape
#

aha yea i found a new job which starts in one month so i have a lot of free time now

unkempt sedge
#

are you going to work for foresight?

pine cape
#

no, it's a quant trading company

unkempt sedge
#

imagine having master peri in my company, godamm. A dream would come true :>

unkempt sedge
#

Question, how can I utilize the replicate once component, to estabilish client start position?

#

I only seen to be able to use default alues

long marsh
#

but you're replicating your player position somehow anyway, yeah? if it's fully sync'd just spawn your client in the right place on the server and replicate as normal

unkempt sedge
#

no I am not I am utilizing physics

long marsh
#

using my fork with my error handling branch atm and getting a lot of this, which i think was just getting swallowed before:

2025-01-29T23:18:19.088420Z ERROR lightyear::client::networking: Error sending packet: netcode error: address 127.0.0.1:12025 address specific transport error  A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. (os error 10040)

which i'm going to guess is only happening on windows 😅

pine cape
#

Hm that's strange, so some packets are not getting sent correctly?

long marsh
#

i think i'm changing my stance on that error even being fatal, too. should just let clients time out normally and suppress the noisy log

pine cape
#

Yes but it's hard to suppress that log; or maybe in receive_packet we can also choose to just not log anything for that error?

long marsh
# pine cape Yes but it's hard to suppress that log; or maybe in `receive_packet` we can also...

yeah i mean this works it's just ugly

fn recv_packets(
    &mut self,
    sender: &mut impl PacketSender,
    receiver: &mut impl PacketReceiver,
) -> Result<()> {
    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();

    // process every packet regardless of success/failure
    loop {
        match receiver.recv() {
            Ok(Some((buf, addr))) => {
                if let Err(e) = self.recv_packet(buf, now, addr, sender) {
                    self.handle_client_error(e);
                }
            }
            Ok(None) => break,
            Err(e) => {
                match &e {
                    TransportError::Io(io_error) => {
                        match io_error.kind() {
                            std::io::ErrorKind::ConnectionReset => {},
                            _ => {
                                self.handle_client_error(e.into());
                            },
                        }
                    },
                    _ => {}
                };
            }
        }
    }

    Ok(())
}

and then that doesn't extrapolate into a strategy for general log suppression; kinda poorly separates concerns doing log filtering inline, idk.

pine cape
#

I can still merge your PR without the windows-specific code for now

long marsh
#

gimme a bit

pine cape
#

sure, take your time

long marsh
sand sigil
#

so in order to display all the "player" entites of clients connecting to the server, i made this display_client function. the problem is I need to call the function using Update (otherwise the function doesnt work properly), and it seems kind of inefficient to be inserting the same components every single update. Is there a way i can get this to execute only after a new client spawns or something?

pub fn display_client(
    mut materials: ResMut<Assets<ColorMaterial>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut commands: Commands,
    player_query: Query<(Entity, &PlayerBundle)>,

) {
    let query = player_query.iter();
    for (entity, player) in query {
        commands.entity(entity).insert((
            Mesh2d(meshes.add(Rectangle::new(50.0, 50.0))),
            MeshMaterial2d(materials.add(ColorMaterial::from_color(player.color))),
            Transform::from_xyz(player.position.x, player.position.y, 1.0),
        ));
    }
    
}

pine cape
#

yes you can do Trigger<OnAdd, PlayerBundle> or Query<(Entity, &PlayerBundle), Added<PlayerBundle>>

pine cape
#

It can. From what I understand (https://partner.steamgames.com/doc/api/ISteamNetworkingSockets#functions_send_recv)
ConnectByIP will just use normal UDP, but ConnectP2P will send packets through Valve's network.

Actually CreateListenSocketP2P (where the server can receive packets relayed by Valve's network) is not supported yet (https://github.com/cBournhonesque/lightyear/blob/23c78ae54a7818b7ae8fb1e38c0350e427bf01f8/lightyear/src/connection/steam/server.rs#L105-L105) so maybe not

GitHub

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

unkempt sedge
#

Are all sub replication components registered?

#

Components such as Replicationtarget group and so on?

pine cape
#

Most of them I believe, but I might have missed some

violet topaz
#

@long marsh just pulled your PR and it's fixed the NetCode error handling, ty vm! 🙂

Now, i'm facing just a minor inconvenience.

  • When the client connects, the server pops a lot of warnings about Client Error: Netcode(ClientNotConnected(Netcode(0))) for a few seconds (While doing a handshake?)
2025-01-30T17:39:51.812779Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
2025-01-30T17:39:51.813374Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
...
2025-01-30T17:39:51.819448Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
2025-01-30T17:39:51.820347Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
2025-01-30T17:39:51.821183Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
2025-01-30T17:39:51.822049Z  WARN lightyear::server::networking: Client Error: Netcode(ClientNotConnected(Netcode(0)))
2025-01-30T17:39:51.823206Z  INFO lightyear::server::connection: New connection from id: Netcode(0)
2025-01-30T17:39:52.023832Z  INFO server: Client 0 connected

Again, tyvm! Amazing Changes!

long marsh
# violet topaz <@158726638144585728> just pulled your PR and it's fixed the NetCode error handl...

yeah it happens here

https://github.com/cBournhonesque/lightyear/blob/84394a7c2a124c5ddb3268529c41bfb770291840/lightyear/src/connection/netcode/server.rs#L746

the client IS in the connection cache, because they started the handshake, but they aren't connected, because they haven't finished it

i was confused about that function originally - is it JUST sending keepalives? it's inappropriate to error there if that's true

#

might make sense to just pull the two calls to handle_client_error out of that function

unkempt sedge
#

Lobbies + managed room + dynamic scenes loading, noticed how the npc entity changes as to reflect the npc entity of that specific user. Worth noting that the wolrd itself is also user defined.

pine cape
#

Cool! Is that black door a portal?

unkempt sedge
#

It is, there is another region in each I intend to display a window to

#

So player can see if someone is spawn camping he

unkempt sedge
pine cape
#

I didn't investigate, it's some non-determinism in avian

unkempt sedge
#

In collision normals?

pine cape
#

in how the contact points are processed, i think

unkempt sedge
#

oh we got lag compensation now? Cool

unique plover
pine cape
#

Thanks I saw, the thing is that I think mapping entities automatically from Confirmed to Predicted is usually the right move

#

I could update entity mapping to just not update the entity at all if the entity is missing in the map

#

I had recently switched to set it to Entity::PLACEHOLDER for easier debugging

unique plover
#

Where is that getting set in source?

pine cape
#

Can you explain more about your usecase?

#

i'm curious to understand why you're only only predicted one entity

#

and you want the component on the predicted entity to map to a confirmed entity

unique plover
#

Yeah, I've got a PlayerAvatarComponent on a Predicted entity, then a ThirdPersonCameraComponent on a entity that is not predicted. They're both spawned by the server and then I transfer authority for the camera to the controlled client. The camera forward is replicated to the server from the camera and then I use the forward to point the PlayerAvatar entity in the controller.

Technically I'm only not predicting on the client that controls the camera, the rest predict it and don't have an issue.

#

The PlayerAvatarComponent is the one with the mapped ThirdPersonCamera.

#
#[derive(Component, Debug, Reflect, Serialize, Deserialize, Clone, PartialEq)]
#[reflect(Component)]
pub struct PlayerAvatarComponent {
    pub player: Option<Entity>,
    pub camera: Option<Entity>,
}

impl MapEntities for PlayerAvatarComponent {
    fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
        self.player = self.player.map(|unmapped| entity_mapper.map_entity(unmapped));
        self.camera = self.camera.map(|unmapped| entity_mapper.map_entity(unmapped));
    }
}
pine cape
#

Aha i don't fully understand your use case

unkempt sedge
#

@pine cape Mr Peri, how do I replicate children entities?

#

Directly from parent

#

I was testing it out it seens is a little broken or I might be missing something

#

Mre sample

pine cape
#

children entities are by default automatically replicated

unkempt sedge
#

Mind checking out what am I doing wrong here

pine cape
#

did you push all your changes?

unkempt sedge
#

Well fuck

#

@primal cargo no more item as children for us

pine cape
#

I can take a look later, it shouldn't be hard

unkempt sedge
#

You see Mr Peri our default pic for ui currently is Mr Shatur and you, if you do that. It will only be you 🙂 hohoho

unique plover
pine cape
#

I recently pushed a couple of big PRs that massively change the way replication/prediction are implemented internally. Normally things should work the same, but please let me know if you notice anything weird/broken on main

plain charm
#

Trying to debug error spam. Is it valid if I give server and client a LOCALHOST addr, and either 0 or like 7777 as the port? Would that cause problems?

pine cape
#

it should work

plain charm
#

let me check it on windows, just in case its macOS

#

Yeah no, I'm stumped. @pine cape Would you mind taking a look?

pine cape
#

what's not working?

plain charm
#

Just connectivity, server-client with multiple apps

#

I'm sure I messed up or forgot a setting somewhere

pine cape
plain charm
#

Ah of course! I had the hostserver configs at Mode::HostServer, but the separate client was also on Mode::HostServer . Needed to be on Mode::Separate

plain charm
# pine cape what's not working?

Don't know if intended or bug:

Disconnecting a hostserver mode local client appears to despawn all locally owned entities with ClientReplicate, too. Should it do that?

pine cape
pine cape
long marsh
#

is there some reason ReplicateOnceComponent would cause the component to replicate zero times? i see there's tests around it so i gotta assume it's user error.

// on server
commands.spawn((
    TestComponent(5),
    ReplicateOnceComponent::<TestComponent>::default(),
    ServerReplicate {
        group: REPLICATION_GROUP_STATIC,
        ..default()
    },
));
app.register_component::<TestComponent>(ChannelDirection::ServerToClient);

above results in TestComponent not on my client entities at all. removing ReplicateOnceComponent makes it replicate fine. thinkfused

pine cape
#

hm yea i'm not sure since the test server::replication::send::tests::test_component_update_replicate_once test seems to run fine

long marsh
pine cape
#

Ah, that's possible. You spawn the entity, the client connects afterwards and the component is not replicated?

long marsh
#

right. looking at server/replicate.rs seems like it’s not handling the new connections any differently when it comes to replicate_once, since it returns early in those cases*.

long marsh
pine cape
#

Thanks I will check it later!

#

The replication logic is very intricate

#

It's easy to miss stuff

tropic jackal
#

Should the MessageRegistry resource be inserted into the app implicitly by Lightyear somewhere? I'm trying to send a message after registering a message via app.register_message and am getting:

Requested resource lightyear::protocol::message::MessageRegistry does not exist in the `World`.
                Did you forget to add it using `app.insert_resource` / `app.init_resource`?
                Resources are also implicitly added via `app.add_event`,
                and can be added by plugins.
pine cape
#

Oh i think i know what happened, it might be due to the message refactor

#

Out of curiosity, could you share your code (it can be via private DM)

#

i think i have a fix but i'd like to know what can trigger this

#

It is added into the app, but there are some commands that temporarily remove the MessageRegistry via resource_scope

tropic jackal
#

Absolutely one second

#

I just sent you a collaborator invite via Github if that's OK, should be able to see everything in a browser

#

If you prefer I DM everything instead lmk

#

Basically network configuration stuff is under:
./client/src/plugins/network
./server/src/plugins/network

And the message I'm trying to register is under
./client/src/plugins/lobby/protocol.rs
./client/src/plugins/lobby/plugin.rs
./client/src/plugins/lobby/systems.rs

unique plover
#

Is it possible to get the id of the client that sent an event from send_event / trigger_event , or is messages still the only way to achieve this?

pine cape
#

Message is the only way to achieve this, since for send_event I directly send the event in Events<E>. But if you have any idea for how the id could still be available i'm all ears

#

I guess creating some wrapper event called FromClient<E> ?

#

@tropic jackal the ClientPlugins and ServerPlugins must be registered before you register any shared plugin (for example the protocol plugin)

#

so this line is causing an issue:
.add_plugins((CameraPlugin, CommonPlugin, LobbyPlugin, NetworkPlugin))
since NetworkPlugin must run before LobbyPlugin

#

Also I don't think your code is going to work, because the ProtocolPlugin that defines the Messages has to be shared between the client and the server

tropic jackal
#

Ahhh very interesting. Thank you for taking a look - yes I don't have any server-side Lobby stuff. Just wanted to see if I could "send" a message without anything blowing up (even if it means nothing will happen).

Thanks again!

unique plover
pine cape
unique plover
#

A trigger on MessageEvent would definitley go a long way for my purposes.

#

I think I'm misunderstanding the utility of event replication without context though. It essentially makes for a "client authoritative" event since there's no way to verify the client that sent it.

#

Server->Client makes sense though.

pine cape
#

Now that i'm thinking about it, yes it doesn't make too much sense.
Some users were asking for some convenience function to seamlessly replicate and send the event E directly. I assume it's because their code was internally reacting on event E via EventReader<E> and they didn't want to write a 'wrapper' message that reads EventReader<MesssageEvent<E>> and then writes the event to EventWriter<MessageEvent<E>>

#

but maybe they should switch to using EventReader<MessageEvent<E>> if they are using networked events

unique plover
#

But yeah I do think Server->Client seamless events without context is totally fine. That is the way that Bevy Replicon handles it, but with a ToClients wrapper for an EventWriter instead of using a connection manager.

pine cape
#

I kind of still want to keep EventReader<MessageEvent<E>> in both directions.
I do support rebroadcasting messages so if client1 sends a message to client2, I would like the MessageEvent on client2 to have message.from = Client1

unique plover
#

Oh, nice. I hadn't seen the _to_target variants of the various send functions yet.

pine cape
#

Hm, the ToClients wrapper for EventWriter is to increase parallelism I'm guessing?
So there's a single system that needs Mut<ConnectionManager>, reads from all the ToClients events and buffers them in the transport; instead of all the systems running sequentially because they use Mut<ConnectionManager?

#

i think that's a good idea, i'll think about it

#

For now my plan is probably to get rid of the send_event() as I'm not sure it's useful.
And then to change trigger_event to trigger a MessageEvent instead of Event

unique plover
#

But I think your assessment still holds.

#

And then it's EventReader<FromClient<MyEvent>>

pine cape
#

yes that's what i meant

plain charm
#

The EntitySpawn event appears to fire off before any of the initially replicated components are added to the entity. This does limit some use cases with ClientReplication. Should the event wait until after, or maybe some other InitialEntityReplicated kind of event should fire after the initial round of replication has been completed?

plain charm
#

Oh, nvm. Found a bunch of workarounds.

pine cape
#

In general I wouldn't rely too much on those events, I'm not sure that they are very useful compared to something like Trigger<OnAdd, Replicated>.

I was actually thinking of removing the events altogether

plain charm
#

Yeah, that makes sense. Do you have an example of applying a simple version of the lerp function for impl Linear? I'm doing a bunch of reading on lerp in general but finding few examples I can use for bevy

#

The component im lerping contains one field, a Vec2, so not too difficult I would think

pine cape
pine cape
# unique plover I may not have been clear enough, it's `EventWriter<ToClients<MyEvent>>`

It's interesting in replicon a given Message/Event is tied to a channel; while in lightyear the two are decoupled, you can send any Message (data) on any Channel (reliability/ordering config)
This does mean that the replicon api is simpler to use (ToClients just contains the message data + destination).
If I were to build a similar API, I would have to think if I want to provide parallelism on the Messages (ToClients<M>) or Channels (ToClients<C>)

#

it's true that in most cases users always send a given Message type on the same channel

unique plover
#

In the case the event is always on a channel would it be something like the following?

app.register_event::<MyChannel, MyEvent>(ChannelDirection::...);
#

This

connection_manager.send_event(ToClients::<MyChannel, _>(MyEvent));

Is a pretty decent API IMO

#

Oh wait, it would be an EventWriter

#
my_writer.send(ToClients::<MyChannel, _>(MyEvent))
#

Hmm, actually Tuple struct wouldn't work there.

pine cape
#

I have idea for how to make it work, but i'm wondering if it's necessary. Most usecases would only want to send a message on a single channel, no?

unique plover
#

I think you're right. Different events for different channels makes sense.

#

I abandoned client authoritative camera in favor of action_state.set_axis_triple() by the way. In hindsight it is the better system. You probably could have kept Entity::PLACEHOLDER for failed mappings after all 😅

#

I currently have a Predicted local player with a Tnua controller and other players are Interpolated. I only get corrections on player collisions which makes sense and is acceptable for me.

pine cape
#

Aha well it might still be useful to have a Predicted component referring to a Confirmed entity; we'll see!

unique plover
#

Which, incidently, it currently isn't possible to Predict the host properly in HostServer mode since host inputs aren't sent to clients.

pine cape
#

you mean that other players cannot predict the host properly?

#

can't the host send inputs to other clients?

unique plover
#

You can replicate an input from client->server->client, but the client that is the host in HostServer mode does not send its inputs to connected clients.

#

You don't even get a host predicted ActionState or InputBuffer on clients.

#

(in v0.19, idk about main)

pine cape
#

Do you have a code example? theoretically lightyear should provide all the tools to make this work

unique plover
#

@empty rampart might have a more readily available example. I obliterated mine in favor of interpolated remote clients. If they don't have one or aren't available I can put one together later.

#

But it was with a replicate_inputs like in the examples and ActionState on the server copy of the entities.

empty rampart
#

I don't have an example available but I mostly followed the avian_3d_character example

pine cape
#

yea i noticed that in host-server mode other clients seem to not be able to predict the host client

unique plover
#

@unkempt sedge also had issues predicting the host? Maybe they have an example ready?

unkempt sedge
#

you can clone our mre_scene, sample and just refactor it to use host server mode

#

but no we dont use host servers

#

As as Mr Shatur once said, the worst thing that can occur i n a game is a server crash. And leaving that to a client is beggin to make it occur

plain charm
#

Having some trouble with change detection on replicated components. I've tried Trigger<OnReplace, MyCoolReplicatedComponent>, Trigger<OnInsert, MyCoolReplicatedComponent>, and Trigger<ComponentUpdateEvent<MyCoolReplicatedComponent>>, but it's not firing.

I've confirmed with debug on lightyear that MyCoolReplicatedComponent is being written across the network at appropriate times

#

Gone through all of the Server and Client variants, just noticed those. Confirmed that changes are indeed happening to MyCoolReplicatedComponent

#

I'm missing something obvious here, I feel it

plain charm
#

Implemented Diffable, Add, and Mul<f32> just in case... still can't get any hits

#

Put in a constantly checking system in Update instead. It's weird I couldn't get the observer to trigger, though

wintry dome
#

happy birthday lightyear 🍰 2 years since first commit on github 🙂

#

i'm about 6 yaks deep at the moment doing other stuff, but will be back on the networking stuff before too long.

pine cape
#

The OnReplace trigger is a bit dangerous for that reason

unkempt sedge
pine cape
#

what struct

unique plover
#

Does rollback only rerun FixedUpdate, or all of FixedMain?

pine cape
#

It runs FixedMain

plain charm
#

If the server has an UNSPECIFIED address, how does anything find it? Both mac and OS return address not valid errors, error code 10049 on windows and 65 on mac. I've checked for firewalls and weird features but I can't figure out why anything that isn't LOCALHOST won't connect.

ERROR lightyear::connection::netcode::client::connection: error updating netcode client: Transport(Io(Os { code: 10049, kind: AddrNotAvailable, message: "The requested address is not valid in its context." }))
#

Only non errors are on Mac, when both server and client are UNSPECIFIED, but it's just

client connecting to server 0.0.0.0:[PORT] [1/1]
client connect failed. connection request timed out
#

pinging 0.0.0.0 results in an unresolved host. What's the idiomatic way UDP sets up remote connections? Is there a hostname thing that's used?

pine cape
#

Not entirely sure; server should use 0.0.0.0:port, and client would then connect to it using 127.0.0.1:port

#

If you're using a remote server, then the firewall/etc. is usually handled for you; so you just set 0.0.0.0:port on the server, and the client uses SERVER_IP:port

plain charm
#

I get the feeling there's some special situation with my setup I haven't found yet.

unique plover
#

I don't think it would be possible otherwise for a system to run fixed, but not as part of rollback.

pine cape
#

You can add the system with run_if(not(is_rollback)) to exclude a system from rollback

long marsh
plain charm
long marsh
plain charm
#

I’ve only been using Ipv4Addr, and I’ve punched in both the private 192.168.0.* addresses as well as the public addresses and even the default gateway. Got to the point where I was throwing whatever at it to see if any hints would come out

long marsh
#

never even bother with public, looping back around to your own router like that is a special kind of hell sometimes

let me go ahead and give you confidence that the 192 addresses are correct. you can ping between them?

plain charm
#

Pings between machines, with the 192’s work as expected. MacOS was being difficult, I needed to give the terminal permission to allow LAN connections

#

I’m not home right now to test anything, and won’t be for a few hours

long marsh
#

mmk, i can actually test lightyear local windows <- -> mac for you too sometime today too. def could be a funny os setting somewhere, or some firewall thing, or etc. i'll see if i can get my project to connect

long marsh
# plain charm I’m not home right now to test anything, and won’t be for a few hours

k when you're back what works for me is

client AND server bind to 0.0.0.0. client can use port 0 (random port), but server binds to specific port.
then just connect the client to server_lan_ip:server_port. (edit: instead of UNSPECIFIED you should also be able to bind to the local device's LAN IP)

i did have to accept lan connections for the terminal session on the mac for it to work. firewall on mac needs to explicitly allow the connection if you have it turned on, but it should ask you

#

in the process i found some bug where if the client disconnects during the handshake the server will never stop trying to send them packets 😭

long marsh
pine cape
#

thanks!

unkempt sedge
#

@pine cape Mr Peri how would you implement a sword collider in multyplayer game? It is transform is set by clikent animations, so I am kinda in a pickle

#

I think this is a case for client rpelication no?

#

As sword positioning is based solely on the colliders position and rotation

pine cape
#

i'm not sure, i would try to do more reading

#

If the sword is predicted you might have to do lag compensation when interacting with other objects/players

unkempt sedge
pine cape
#

yeah but that is happening on the predicted entity, I presume

#

You could also try to replicate each bone, etc. from the server

unkempt sedge
#

but you might be right indeed i need to think this through

pine cape
#

@long marsh in your example, weirdly the client never calls the on_disconnect system which should trigger whenever the state changes to NetworkingState::Disconnecting. I'm not sure why

long marsh
pine cape
#

then the server would time out the client after a while

#

(but in your test the client keeps sending pings which prevents timeout)

long marsh
#

is there a way for me to murder the client in a test if we wanted to prove that? that alt+f4'd clients mid-handshake time out correctly?

curious about the actual test i gave you though...

pine cape
#

yeah i'm really surprised that this happens. I was thinking that it was because somehow the client was already in the Disconnecting state. (OnEnter doesn't trigger if you re-enter from the same state, but it doesn't seem to be the case)

pine cape
plain charm
unkempt sedge
#

@pine cape I figured out a very decent solution Mr Peri, I made it so my collider is a bullet and it follows a pre-estabilished path. Of transforms. So now I have high precision + common pre predicted + server spawn logic.

pine cape
#

Great, so you don't need to replicate the bullet after it first fires

#

That's also what i do in my fps example

unkempt sedge
pine cape
pine cape
#

I don't understand what you mean

unkempt sedge
#

Yes I see how my little stroke of a phrasing sounds confusing hohoho

unkempt sedge
pine cape
#

There should be no rollbacks on spawn for pre-predicted entities; if you have some it's a bug

#

I have changed a bunch of things recently so it's possible that this bug has been re-introduced

unkempt sedge
#

hmm might be just ought curiosity in the spaceship sample we reintroduce a physical body to bullet https://github.com/cBournhonesque/lightyear/blob/f677ed9d6bfca9bd2c6485e757a00831319d464d/examples/spaceships/src/client.rs#L57
Here any ideas why? I believe the pre predicted entity already contains the physical elements no?

GitHub

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

unkempt sedge
pine cape
#

Idk, the exampel seems buggy; how come we don't fire the bullet here if identity.is_client()?

unkempt sedge
#

oh no with we turn it off there is no collider, wut

#

oh the system doesnt run when it has rollbacks

#

which is basically always when we have two entities

#

due to collisions

unkempt sedge
#

@pine cape I believe we are having rollbacks with pre predicted spawn, if you would like I have a somewhat of a mre that I can show you. That displays the bug. Problem is it is a private repo

pine cape
#

I can probably reproduce it with the examples already; i'm busy with some other stuff but i can take a look later

unkempt sedge
long marsh
#

any guesses why my correction_fn for Transform is making my player jitter?

// this jitters
.add_correction_fn(|start, end, t| {
    lerp(start, end, t)
})
// this works perfect
.add_correction_fn(|start, end, t| {
    end.clone()
})

the lerping correction fn seems to work fine on my projectiles. just makes the player jitter. thinkfused

pine cape
#

@unkempt sedge I think i'm close to having a PR that will fix replicating with rooms/interest-management and parent-children hierarchy, but it will only work with bevy main

pine cape
unkempt sedge
long marsh
#

even when just holding a single direction

#

god i hope it’s not because im using LWIM main branch or something (edit: nope, not any of my forked deps thinkfused )

pine cape
#

nope it's probably lightyear; i would try to log the player position before/after correction to see what's happening, but it's kind of tedious

long marsh
#

oh interesting. i have a custom LWIM axis input that makes my character turn to face the mouse. disabling that input entirely (or booting the game and doing zero turn inputs) fixes the position jitter. oof

#

but it doesn't matter if i'm actively rotating, just if i've rotated at all since the game started. crimeny

long marsh
#

switching to replicating Position and Rotation over Transform fixes it 👍

#

can i tune the duration corrections are smeared over?

fervent karma
#

Hi, I've been trying to move over to using PreSpawnedPlayerObject for some objects that really should be (previously I just had them spawn late because it was easier, but I'm trying to clean up my implementation), but I'm having a bit of an issue with double images.
As we discussed in #networking I now have some system that work the same way on client and server, and one of these is the system that adds a sprite to the "minion" that I'm trying to prespawn. So I have this filter that checks for Predicted, Interpolated, and Replicating, and that works great for the most part, but when using PreSpawnedPlayerObject the visuals don't appear until the Predicted entity is created, so I tried adding PreSpawnedPlayerObject to the filter, and that works as expected, it creates visuals immediately, which is exactly what I want, but then the moment the replication comes through it creates a double image, and then after a little while the PreSpawnedPlayerObject seems to be despawned, so it seems like maybe the prespawning isn't quite able to match to the right entity maybe?

#

Ah, I think I figured it (and another issue I happened to have with Replicating overwriting my work, presumably because of mis-predictions). The change was as simple as changing Time to Time<Fixed>, however this is kiind of bad, because that's supposed to be happening automatically. Maybe something to do with the way the FixedUpdate system is ran causes Time sometimes not to be Time<Fixed>? Regardless it makes perfect sense that relying on real Time would cause mispredictions.
Looking in the egui Inspector I still see the PreSpawnedPlayerObject disappearing, but maybe that's expected?

long marsh
#

that’s expected

#

i checked the Time thing and it looks ok in my systems using the regular one

fervent karma
#

Hm, this project is still on Bevy 14, so maybe it's a bevy version thing

fervent karma
#

Is there a way to fix the pre-spawned object from disappearing when the replication kicks in? It's only a a few hundred milliseconds depending on ping, but in that space between spawning and replication, if I select the minion then it gets unselected the moment replication kicks in and deletes that entity

long marsh
pine cape
#

I know for sure that PreSpawning was not causing any rollbacks a few weeks ago, some of my recent changes must have broken it

pine cape
# long marsh switching to replicating `Position` and `Rotation` over `Transform` fixes it 👍

Ah that's good to know. I've also switched to replicating Position/Rotation instead of Transform because Transform was causing a bunch of issues.
Those are pretty subtle, I should document them somewhere. I suspect that it's because the VisualInterpolation system restores the Transform value in PreUpdate, and some of the avian systems rely on GlobalTransform, which hasn't been updated. Maybe running an extra transform_propagate system in PreUpdate after VisualInterpolation restore would fix the issue
You can check the settings I use here: https://github.com/cBournhonesque/sixdof which doesn't have any rollback (except on collisions, but that's an avian issue)

GitHub

Multiplayer fps game made in bevy using Quake maps - cBournhonesque/sixdof

pine cape
# fervent karma Hi, I've been trying to move over to using PreSpawnedPlayerObject for some objec...

Ok I just read this;

  1. PreSpawning just works in FixedUpdate because we need to have access to correct tick information. FixedUpdate systems should all use Time<Fixed>
  2. The FPS example (https://github.com/cBournhonesque/lightyear/tree/main/examples/fps) does exactly what you mention with no rollback or despawning. If the PreSpawnedPlayerObject is despawned, that means that there was no match and the server-replicated entity was simply normally Predicted. This is also why you're seeing 2 images; if the matching was working you would just see one because the server-replicated entity would be merged inside the client pre-spawned entity.
    If we don't find any match, we just despawn the entity after a short while. So basically I don't think the PreSpawn systems are currently working for you.
    Have you tried using the visualizer feature to get more information about the matches? Or you could also print the PreSpawn hash on both client/server to see if it's the same
GitHub

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

long marsh
#

oh, indeed, my disappearing projectiles problem seems to be a problem with my rotation systems causing rollbacks still

long marsh
#

fixed that! there we go. now i just have my projectiles causing rollbacks when i shoot them, and they are stuttery 🥲

fervent karma
#

I'll try out the vizualiser, which I hadn't heard about, and I'll check the hashes, how exactly does the hashes work? Given that the entity will likely be in a different location, it can't be based on position, right?