server: Implement player count for status ping (#352) · feather-rs/feather@adf5f4f · GitHub
Skip to content

Commit adf5f4f

Browse files
authored
server: Implement player count for status ping (#352)
1 parent 4c78806 commit adf5f4f

7 files changed

Lines changed: 171 additions & 10 deletions

File tree

Cargo.lock

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

crates/server/Cargo.toml

Lines changed: 1 addition & 0 deletions

crates/server/src/connection_worker.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::{fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
22

3+
use base::Text;
34
use flume::{Receiver, Sender};
45
use futures_lite::FutureExt;
56
use io::ErrorKind;
67
use protocol::{
7-
codec::CryptKey, ClientPlayPacket, MinecraftCodec, Readable, ServerPlayPacket, Writeable,
8+
codec::CryptKey, packets::server::Disconnect, ClientPlayPacket, MinecraftCodec, Readable,
9+
ServerPlayPacket, Writeable,
810
};
911
use tokio::{
1012
io::{AsyncReadExt, AsyncWriteExt},
@@ -18,6 +20,7 @@ use tokio::{
1820
use crate::{
1921
initial_handler::{InitialHandling, NewPlayer},
2022
options::Options,
23+
player_count::PlayerCount,
2124
};
2225

2326
/// Tokio task which handles a connection and processes
@@ -32,6 +35,7 @@ pub struct Worker {
3235
reader: Reader,
3336
writer: Writer,
3437
options: Arc<Options>,
38+
player_count: PlayerCount,
3539
packets_to_send_tx: Sender<ServerPlayPacket>,
3640
received_packets_rx: Receiver<ClientPlayPacket>,
3741
new_players: Sender<NewPlayer>,
@@ -42,6 +46,7 @@ impl Worker {
4246
stream: TcpStream,
4347
_addr: SocketAddr,
4448
options: Arc<Options>,
49+
player_count: PlayerCount,
4550
new_players: Sender<NewPlayer>,
4651
) -> Self {
4752
let (reader, writer) = stream.into_split();
@@ -55,6 +60,7 @@ impl Worker {
5560
reader,
5661
writer,
5762
options,
63+
player_count,
5864
packets_to_send_tx,
5965
received_packets_rx,
6066
new_players,
@@ -75,12 +81,22 @@ impl Worker {
7581
}
7682
}
7783

78-
async fn proceed(self, result: InitialHandling) {
84+
async fn proceed(mut self, result: InitialHandling) {
7985
match result {
8086
InitialHandling::Disconnect => (),
8187
InitialHandling::Join(new_player) => {
88+
if self.player_count.try_add_player().is_err() {
89+
self.write(ServerPlayPacket::Disconnect(Disconnect {
90+
reason: Text::from("The server is full!").to_string(),
91+
}))
92+
.await
93+
.ok();
94+
return;
95+
}
96+
97+
let username = new_player.username.clone();
8298
let _ = self.new_players.send_async(new_player).await;
83-
self.split();
99+
self.split(username);
84100
}
85101
}
86102
}
@@ -89,6 +105,10 @@ impl Worker {
89105
&self.options
90106
}
91107

108+
pub fn player_count(&self) -> u32 {
109+
self.player_count.get()
110+
}
111+
92112
#[allow(unused)]
93113
pub fn enable_compression(&mut self, threshold: usize) {
94114
self.reader.codec.enable_compression(threshold);
@@ -108,16 +128,23 @@ impl Worker {
108128
self.writer.write(packet).await
109129
}
110130

111-
pub fn split(self) {
112-
let Self { reader, writer, .. } = self;
131+
pub fn split(self, username: String) {
132+
let Self {
133+
reader,
134+
writer,
135+
player_count,
136+
..
137+
} = self;
113138
let reader = tokio::task::spawn(async move { reader.run().await });
114139
let writer = tokio::task::spawn(async move { writer.run().await });
115140

116141
tokio::task::spawn(async move {
117142
let result = reader.race(writer).await.expect("task panicked");
118143
if let Err(e) = result {
119-
log::error!("Connection lost: {:?}", e);
144+
let message = disconnected_message(e);
145+
log::debug!("{} lost connection: {}", username, message);
120146
}
147+
player_count.remove_player();
121148
});
122149
}
123150

@@ -208,3 +235,12 @@ impl Writer {
208235
Ok(())
209236
}
210237
}
238+
239+
fn disconnected_message(e: anyhow::Error) -> String {
240+
if let Some(io_error) = e.downcast_ref::<io::Error>() {
241+
if io_error.kind() == ErrorKind::UnexpectedEof {
242+
return "disconnected".to_owned();
243+
}
244+
}
245+
format!("{:?}", e)
246+
}

crates/server/src/initial_handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct Version {
7878
#[derive(Debug, Serialize)]
7979
struct Players {
8080
max: u32,
81-
online: usize,
81+
online: u32,
8282
}
8383

8484
async fn handle_status(worker: &mut Worker) -> anyhow::Result<InitialHandling> {
@@ -91,7 +91,7 @@ async fn handle_status(worker: &mut Worker) -> anyhow::Result<InitialHandling> {
9191
},
9292
players: Players {
9393
max: worker.options().max_players,
94-
online: 0, // TODO (1.16): player count
94+
online: worker.player_count(),
9595
},
9696
description: Text::from(worker.options().motd.clone()),
9797
favicon: worker

crates/server/src/lib.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ mod listener;
2121
mod network_id_registry;
2222
mod options;
2323
mod packet_handlers;
24+
mod player_count;
2425
mod systems;
2526

2627
pub use client::{Client, ClientId, Clients};
2728
pub use network_id_registry::NetworkId;
2829
pub use options::Options;
30+
use player_count::PlayerCount;
2931
use systems::view::WaitingChunks;
3032

3133
/// A Minecraft server.
@@ -44,6 +46,8 @@ pub struct Server {
4446
chunk_subscriptions: ChunkSubscriptions,
4547

4648
last_keepalive_time: Instant,
49+
50+
player_count: PlayerCount,
4751
}
4852

4953
impl Server {
@@ -52,9 +56,10 @@ impl Server {
5256
/// Must be called within the context of a Tokio runtime.
5357
pub async fn bind(options: Options) -> anyhow::Result<Self> {
5458
let options = Arc::new(options);
59+
let player_count = PlayerCount::new(options.max_players);
5560

5661
let (new_players_tx, new_players) = flume::bounded(4);
57-
Listener::start(Arc::clone(&options), new_players_tx).await?;
62+
Listener::start(Arc::clone(&options), player_count.clone(), new_players_tx).await?;
5863

5964
log::info!(
6065
"Server is listening on {}:{}",
@@ -69,6 +74,7 @@ impl Server {
6974
waiting_chunks: WaitingChunks::default(),
7075
chunk_subscriptions: ChunkSubscriptions::default(),
7176
last_keepalive_time: Instant::now(),
77+
player_count,
7278
})
7379
}
7480

@@ -78,6 +84,11 @@ impl Server {
7884
systems::register(self, game, systems);
7985
game.add_entity_spawn_callback(entities::add_entity_components);
8086
}
87+
88+
/// Gets the number of online players.
89+
pub fn player_count(&self) -> u32 {
90+
self.player_count.get()
91+
}
8192
}
8293

8394
/// Low-level functions, mostly used internally.

crates/server/src/listener.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ use anyhow::Context;
44
use flume::Sender;
55
use tokio::net::{TcpListener, TcpStream};
66

7-
use crate::{connection_worker::Worker, initial_handler::NewPlayer, options::Options};
7+
use crate::{
8+
connection_worker::Worker, initial_handler::NewPlayer, options::Options,
9+
player_count::PlayerCount,
10+
};
811

912
/// Listens for and accepts incoming connections.
1013
pub struct Listener {
1114
listener: TcpListener,
1215
options: Arc<Options>,
16+
player_count: PlayerCount,
1317
new_players: Sender<NewPlayer>,
1418
}
1519

1620
impl Listener {
1721
pub async fn start(
1822
options: Arc<Options>,
23+
player_count: PlayerCount,
1924
new_players: Sender<NewPlayer>,
2025
) -> anyhow::Result<()> {
2126
let listener = TcpListener::bind(format!("{}:{}", options.bind_address, options.port))
@@ -25,6 +30,7 @@ impl Listener {
2530
let listener = Listener {
2631
listener,
2732
options,
33+
player_count,
2834
new_players,
2935
};
3036
tokio::task::spawn(async move {
@@ -47,6 +53,7 @@ impl Listener {
4753
stream,
4854
addr,
4955
Arc::clone(&self.options),
56+
self.player_count.clone(),
5057
self.new_players.clone(),
5158
);
5259
worker.start();

crates/server/src/player_count.rs

Lines changed: 105 additions & 0 deletions

0 commit comments

Comments
 (0)