Skip to content

Commit 75d0136

Browse files
committed
better client id structure(s).
1 parent 2fc2d2c commit 75d0136

File tree

7 files changed

+52
-39
lines changed

7 files changed

+52
-39
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ fstr = { path = "../crates/fstr"}
1616
replays = { path = "../replays" }
1717
glam = "0.30.5"
1818
enumset = "1.1.10"
19+
slotmap = "1.0.7"

server/src/network/client.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ use std::collections::HashMap;
33
use anyhow::bail;
44
use bytes::{Buf, Bytes, BytesMut};
55
use fstr::FString;
6+
use slotmap::new_key_type;
67
use tokio::{io::{self, AsyncReadExt, AsyncWriteExt}, net::{TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}}, sync::mpsc::UnboundedSender, task::JoinHandle};
78
use uuid::Uuid;
89

910
use crate::{ClientId, GameProfile, GameProfileProperty, network::{binary::var_int::{VarInt, peek_var_int}, connection_state::ConnectionState, internal_packets::{MainThreadMessage, NetworkThreadMessage}, packets::{packet_buffer::PacketBuffer, packet_deserialize::PacketDeserializable}, protocol::{handshake::serverbound::Handshake, login::{clientbound::LoginSuccess, serverbound::LoginStart}, play::serverbound::Play, status::{clientbound::{StatusPong, StatusResponse}, serverbound::StatusPing}}}, types::status::StatusBytes};
1011

12+
new_key_type! {
13+
pub struct ClientKey;
14+
}
15+
1116
#[derive(Debug, Clone)]
1217
pub struct Client {
1318
pub id: ClientId,

server/src/network/network.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
use crate::network::client::{ClientHandler};
1+
use crate::network::client::{ClientHandler, ClientKey};
22
use crate::network::connection_state::ConnectionState;
33
use crate::types::status::Status;
44
use core::panic;
5-
use std::collections::HashMap;
5+
use slotmap::SlotMap;
66
use tokio::net::TcpListener;
77
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
88

99
use crate::network::internal_packets::{MainThreadMessage, NetworkThreadMessage};
10-
use crate::player::player::ClientId;
1110

1211
type Sender<T> = UnboundedSender<T>;
1312
type Receiver<T> = UnboundedReceiver<T>;
1413

15-
type ClientMap = HashMap<ClientId, ClientHandler>;
1614

1715
pub fn start_network(
1816
ip: &'static str,
@@ -34,21 +32,20 @@ async fn run_network_thread(
3432
let listener = TcpListener::bind(ip).await.unwrap();
3533
println!("Network thread listening on {ip}");
3634

37-
let mut clients: ClientMap = HashMap::new();
38-
let mut client_id_counter: ClientId = 1;
39-
35+
// slotmap is faster than a hashmap and works just as well for us here
36+
let mut clients: SlotMap<ClientKey, ClientHandler> = SlotMap::with_key();
37+
4038
loop {
4139
tokio::select! {
4240
// a client failing to connect here is recoverable and doesnt really do anything, so we can just ignore it.
4341
// we do need to continue on a failed connection though, otherwise it would need to wait for network_rx to receive
4442
// before attempting to get a new connection.
4543
result = listener.accept() => {
4644
let Ok((socket, _)) = result else { continue };
47-
let client_id: ClientId = client_id_counter;
48-
client_id_counter += 1;
4945

50-
let handler = ClientHandler::spawn(client_id, socket, network_tx.clone(), main_tx.clone(), status.get());
51-
clients.insert(client_id, handler);
46+
clients.insert_with_key(|key| {
47+
ClientHandler::spawn(key, socket, network_tx.clone(), main_tx.clone(), status.get())
48+
});
5249
}
5350

5451
// this can never be none since this function owns a network_tx.
@@ -58,27 +55,27 @@ async fn run_network_thread(
5855
match msg {
5956
NetworkThreadMessage::UpdateStatus(update) => status.set(update),
6057
NetworkThreadMessage::SendPackets { client_id, buffer } => {
61-
if let Some(handler) = clients.get_mut(&client_id) {
58+
if let Some(handler) = clients.get_mut(client_id) {
6259
if let Err(e) = handler.send(&buffer).await {
63-
eprintln!("Client {} handler failed to send: {}", client_id, e);
64-
clients.remove(&client_id);
60+
eprintln!("Client {:?} handler failed to send: {}", client_id, e);
61+
clients.remove(client_id);
6562
main_tx.send(MainThreadMessage::ClientDisconnected { client_id }).expect("Main thread should never drop its network reciever.");
6663
}
6764
}
6865
}
6966

7067
NetworkThreadMessage::DisconnectClient { client_id } => {
71-
if let Some(handler) = clients.remove(&client_id) {
68+
if let Some(handler) = clients.remove(client_id) {
7269
if let Err(e) = handler.disconnect().await {
73-
eprintln!("Client {} writer failed to shutdown: {}", client_id, e);
70+
eprintln!("Client {:?} writer failed to shutdown: {}", client_id, e);
7471
}
7572
main_tx.send(MainThreadMessage::ClientDisconnected { client_id }).expect("Main thread should never drop its network reciever.");
7673
}
7774
}
7875

7976
NetworkThreadMessage::ConnectionClosed { client_id, connection_state } => {
8077
// we probably shouldnt tell the main thread a client it never added got disconnected?
81-
if clients.remove(&client_id).is_some() && connection_state == ConnectionState::Play {
78+
if clients.remove(client_id).is_some() && connection_state == ConnectionState::Play {
8279
main_tx.send(MainThreadMessage::ClientDisconnected { client_id }).expect("Main thread should never drop its network reciever.");
8380
}
8481
}

server/src/player/player.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::inventory::item_stack::ItemStack;
55
use crate::inventory::menu::OpenContainer;
66
use crate::inventory::Inventory;
77
use crate::network::binary::var_int::VarInt;
8+
use crate::network::client::ClientKey;
89
use crate::network::packets::packet::IdentifiedPacket;
910
use crate::network::packets::packet_buffer::PacketBuffer;
1011
use crate::network::packets::packet_serialize::PacketSerializable;
@@ -24,7 +25,7 @@ use std::f32::consts::PI;
2425
use std::ptr::NonNull;
2526
use uuid::Uuid;
2627

27-
pub type ClientId = usize;
28+
pub type ClientId = ClientKey;
2829

2930
#[derive(Debug, Clone)]
3031
pub struct GameProfileProperty {

server/src/world/chunk/chunk.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::network::packets::packet_buffer::PacketBuffer;
44
use crate::network::protocol::play::clientbound::ChunkData;
55
use crate::player::player::ClientId;
66
use glam::DVec3;
7+
use slotmap::SparseSecondaryMap;
78
use std::collections::HashSet;
89

910
pub struct ChunkSection {
@@ -15,6 +16,8 @@ pub struct Chunk {
1516
pub packet_buffer: PacketBuffer,
1617

1718
pub players: HashSet<ClientId>,
19+
// this was the same behavior as before though so maybe it doesnt matter.
20+
pub players: SparseSecondaryMap<ClientId, ()>,
1821
pub entities: HashSet<EntityId>,
1922

2023
// contains the chunk data packet,
@@ -27,11 +30,10 @@ pub struct Chunk {
2730

2831
impl Chunk {
2932

30-
pub fn new() -> Self {
31-
Self {
32-
chunk_sections: [const { None }; 16],
33+
pub fn players: HashSet::new(),
34+
chunk_sections: [const { None }; 16],
3335
packet_buffer: PacketBuffer::new(),
34-
players: HashSet::new(),
36+
players: SparseSecondaryMap::new(),
3537
entities: HashSet::new(),
3638

3739
cached_chunk_data: PacketBuffer::new(),
@@ -121,23 +123,19 @@ impl Chunk {
121123
bitmask,
122124
data,
123125
});
124-
self.dirty = false;
125-
}
126-
into.copy_from(&self.cached_chunk_data);
127-
}
128-
pub fn insert_player(&mut self, client_id: ClientId) {
129-
debug_assert!(!self.players.contains(&client_id), "player already in chunk");
126+
self debug_assert!(!self.players.contains(&client_id), "player already in chunk");
130127
self.players.insert(client_id);
128+
: ClientId) {
129+
debug_assert!(!self.players.contains_key(client_id), "player already in chunk");
130+
self.players.insert(client_id, ());
131131
}
132132

133133
pub fn insert_entity(&mut self, entity_id: EntityId) {
134-
debug_assert!(!self.entities.contains(&entity_id), "entity already in chunk");
135-
self.entities.insert(entity_id);
136-
}
137-
138-
pub fn remove_player(&mut self, client_id: ClientId) {
139-
debug_assert!(self.players.contains(&client_id), "player was never in this chunk");
134+
debug_assert!(!self.entities.contains(& debug_assert!(self.players.contains(&client_id), "player was never in this chunk");
140135
self.players.remove(&client_id);
136+
ClientId) {
137+
debug_assert!(self.players.contains_key(client_id), "player was never in this chunk");
138+
self.players.remove(client_id);
141139
}
142140

143141
pub fn remove_entity(&mut self, entity_id: EntityId) {

server/src/world/world.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::world::chunk::chunk_grid::ChunkGrid;
1212
use crate::world::chunk::get_chunk_position;
1313
use enumset::EnumSet;
1414
use glam::{DVec3, Vec3};
15+
use slotmap::SecondaryMap;
1516
use std::collections::HashMap;
1617
use std::ops::{Deref, DerefMut};
1718
use tokio::sync::mpsc::UnboundedSender;
@@ -34,9 +35,9 @@ pub trait WorldExtension : Sized {
3435

3536
pub struct World<E : WorldExtension> {
3637
pub network_tx: UnboundedSender<NetworkThreadMessage>,
37-
38+
3839
pub players: Vec<Player<E::Player>>,
39-
pub player_map: HashMap<ClientId, usize>,
40+
pub player_map: SecondaryMap<ClientId, usize>,
4041
pub npc_profiles: HashMap<&'static str, GameProfile>,
4142

4243
entity_id: i32,
@@ -58,7 +59,7 @@ impl<E : WorldExtension> World<E> {
5859
Self {
5960
network_tx,
6061
players: Vec::new(),
61-
player_map: HashMap::new(),
62+
player_map: SecondaryMap::new(),
6263
npc_profiles,
6364
entity_id: 0,
6465
entities: Vec::new(),
@@ -214,7 +215,7 @@ impl<E : WorldExtension> World<E> {
214215
// order isn't preserved doing this.
215216
// however with old implementation, it did use std hashmap to iterate,
216217
// and order wasn't preserved there so it should be fine.
217-
if let Some(index) = self.player_map.remove(&client_id) {
218+
if let Some(index) = self.player_map.remove(client_id) {
218219
let last_index = self.players.len() - 1;
219220
let player = self.players.swap_remove(index);
220221

@@ -289,7 +290,7 @@ impl<E : WorldExtension> World<E> {
289290
let _ = self.network_tx.send(NetworkThreadMessage::UpdateStatus(StatusUpdate::Players(self.players.len() as u32)));
290291
}
291292
MainThreadMessage::PacketReceived { client_id, packet } => {
292-
if let Some(index) = self.player_map.get(&client_id) {
293+
if let Some(index) = self.player_map.get(client_id) {
293294
let player = &mut self.players[*index];
294295
packet.process(player)
295296
}

0 commit comments

Comments
 (0)