-
Notifications
You must be signed in to change notification settings - Fork 0
Usernames #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Usernames #69
+558
−104
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cb341
commented
Jan 25, 2026
cb341
commented
Jan 25, 2026
cb341
commented
Jan 25, 2026
cb341
commented
Jan 25, 2026
cb341
commented
Jan 25, 2026
72d59cf to
e8ae125
Compare
6b4b938 to
1c862e5
Compare
cb341
commented
Jan 25, 2026
cb341
commented
Jan 26, 2026
| self.as_str() == SERVER_USERNAME | ||
| } | ||
|
|
||
| pub fn to_netcode_user_data(&self) -> [u8; NETCODE_USER_DATA_BYTES] { |
Owner
Author
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stole this snippet from their example project:
https://github.com/lucaspoffo/renet/blob/master/demo_chat/src/main.rs#L51-L70
cb341
commented
Jan 26, 2026
cb341
commented
Jan 26, 2026
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Introduce Usernames
Player usernames and better state management.
ClientIdtoUsernamein Chat and other constructsThis feature was suprisingly time consuming to implement as there were many bugs with the old implementation of client/server networking.
Impl
Chat messages and player positions were originally identified by unique client ids.
The
ClientIds are generated on the start of the client binary and are randomly generated.When the client disconnected from the server, the player state (position,rotation) would be dropped as the client cannot reconnect with the same unique id.
Usernames on the other hand, allow us to store player state and have players rejoin and keep their state.
We cannot construct
ClientIdfrom username hash for example, because Renet doesn't handle non-unique client connections.https://github.com/lucaspoffo/renet/blob/631fc8addc178a8f8bbb215f19a7cc471b8abb5f/renet/src/server.rs#L35-L49
When connecting to the server we can pass additional 250 ish bytes in a user_data byte array
https://github.com/lucaspoffo/renet/blob/631fc8addc178a8f8bbb215f19a7cc471b8abb5f/demo_chat/src/main.rs#L51-L70
https://github.com/lucaspoffo/renet/blob/master/renetcode/src/client.rs#L43-L43
Imo 250 bytes for a username is too much so I capped it at 50.
In the chat demo they created a wrapper type for a String. Strings are heap allocated and cannot be easily copied. I was very annyed by the explicit clones everywhere, so I decided to represent Usernames as byte arrays 6f3f8da. In addition to being more readable it should be faster (although this has no real world impact, the code readability does).
The username should be somehow passable by the user and not be randomly generated. Two options:
clapas a dependency)StartScreenstate or something (difficult)I decided to go with
Then I updated all data structures that previously used ClientId as a key to use the new Username instead.
I noticed that for
ChatMessageI was using0to represent the SERVER sender. This would be stupid withUsernameso I replaced it with a proper enum.I spent quite some time digging around Renet because I had issues with clients not disconnecting properly and apparently this has been a historic issue as well:
client.disconnect()breaks renet lucaspoffo/renet#68When using CTRL+C, the app exit event is triggered, client sends disconnect message to server.
When closing a window via controls, app exit is triggered and disconnect message is sent.
But when closing a window via CMD+Q, no app exit is emmited and the server thinks the client is still connected only realizing the opposite is true after something like a 15 second interval.
It turns out that Bevy uses
AppExitErrors to indicate a graceful shutdown.When using CTRL+C you need to have the
TerminalCtrlCHandlerPlugininstalled.Ctrl+Cin the terminal properly bevyengine/bevy#14001This is usually included by the
DefaultPlugins:https://docs.rs/bevy/latest/bevy/app/struct.TerminalCtrlCHandlerPlugin.html
Then I wanted to show the text above the remote player, so I looked for crates.
This one looked like the best pick: https://github.com/kisya-games/bevy_mod_billboard.
When setting up Billboard, I struggled with the text component:
eckz/bevy_flair#38
Also: The relative text position was somehow not adjustable by adding a Transform::Translate3d to the Node bundle so I just decided to add a few blanklines.
Before:
After:
Problems with Networking.
In the flow I want the Client to ask the server for permission to join.
The server receives the
ServerEvent::ClientConnectedevent, checks if another client has already picked the username, if so the new client get's rejected. The problem was that I wanted to have the client receive the Reject event as well as disconnect the client at the server. When disconnecting the client immediately, it wouldn't receive the Reject event and wouldn't behave properly. Thus I introduced a queue. You can queue new clients to be disconnected and you can retain ready clients. this guarantees that events such as Reject get sent properly before we terminate the connection.I also needed a efficient DataStructure to map ClientIds to Usernames and back, so I decided to go with a bidirectional HashMap. It is a wrapper around two HashMaps. When inserting a
ClientId,Usernamepair, I update both tables:There is a memory leak, so if you start your client, kill it and restart it over and over again, the server allocates another 51*2 bytes every time as the
ClientId→Usernamemappings aren't collected.Adding the spawn state was pretty easy as I could just delegate the spawn point to the accept event and add another resource.
Lessons
Copytrait are very nice to work with.Other
Made with ❤️