lucaspoffo / renet

Server/Client network library for multiplayer games with authentication and connection management made with Rust
Apache License 2.0
624 stars 67 forks source link

Allow impl of Framed for cleaner message framing #44

Closed Kaendor closed 1 year ago

Kaendor commented 1 year ago

Hello,

I'm using Renet for about 3 weeks now, overall great usability but one thing is a bit frustrating. I think the client and server APIs should use specific messages instead of generic vectors of bytes.

One way around it could be to impl Framed from Tokio or maybe it is overkill. This way, one would have to pass a codec at creation and have a nicely typed frames!

Shatur commented 1 year ago

I'm not the author, but as a user I'm fine with the current API. You can use any serializer you want. Just create your own enum and on deserialization you will know the exact type of the received message.

Kaendor commented 1 year ago

Yes and that's what I am doing at the moment. It's a convenience issue I believe.

lucaspoffo commented 1 year ago

This is something that I tested in early development, but in the end, it was too hard to make generic or usable for all use cases. This happens mainly because RenetServer/Client is more than a stream, and each channel can have a different approach for decoding/encoding.

For example, I have one channel that receives messages that use bincode for serializing/deserializing and another channel that uses bit_serializer to send snapshots of the world state.

What I can suggest is to implement helpers functions for that, if you're using bincode could be something like:

fn receive_client_message<T: DeserializeOwned>(server: &mut RenetServer, client_id: u64, channel: Into<u8>) -> Option<T> {
    if let Some(message) = server.receive_message(client_id, channel) {
        match bincode::deserialize::<T>(&message) {
            Ok(message) => return Some(message),
            Err(e) => log::error!("Failed to deserialize message: {}", e),
        }
    }

    None
}

fn receive_server_message<T: DeserializeOwned>(client: &mut RenetClient, channel: Into<u8>) -> Option<T> {
    if let Some(message) = client.receive_message(channel) {
        match bincode::deserialize::<T>(&message) {
            Ok(message) => return Some(message),
            Err(e) => log::error!("Failed to deserialize message: {}", e),
        }
    }

    None
}

You could do the same thing for sending messages. Maybe adding these in the examples could help people make their own helpers.