-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathlib.rs
More file actions
147 lines (127 loc) · 4.54 KB
/
lib.rs
File metadata and controls
147 lines (127 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![allow(clippy::unnecessary_wraps)] // systems are required to return Results
use std::{sync::Arc, time::Instant};
use base::Position;
use chunk_subscriptions::ChunkSubscriptions;
use common::Game;
use ecs::SystemExecutor;
use flume::Receiver;
use initial_handler::NewPlayer;
use listener::Listener;
mod chunk_subscriptions;
pub mod client;
pub mod config;
mod connection_worker;
mod entities;
pub mod favicon;
mod initial_handler;
mod listener;
mod network_id_registry;
mod options;
mod packet_handlers;
mod player_count;
mod systems;
pub use client::{Client, ClientId, Clients};
pub use network_id_registry::NetworkId;
pub use options::Options;
use player_count::PlayerCount;
use systems::view::WaitingChunks;
/// A Minecraft server.
///
/// Call [`link_with_game`](Server::link_with_game) to register the server
/// with a [`Game`](common::Game). This will
/// cause the server to serve the game to players.
///
/// Uses asynchronous IO with Tokio.
pub struct Server {
options: Arc<Options>,
clients: Clients,
new_players: Receiver<NewPlayer>,
waiting_chunks: WaitingChunks,
chunk_subscriptions: ChunkSubscriptions,
last_keepalive_time: Instant,
player_count: PlayerCount,
}
impl Server {
/// Starts a server with the given `Options`.
///
/// Must be called within the context of a Tokio runtime.
pub async fn bind(options: Options) -> anyhow::Result<Self> {
let options = Arc::new(options);
let player_count = PlayerCount::new(options.max_players);
let (new_players_tx, new_players) = flume::bounded(4);
Listener::start(Arc::clone(&options), player_count.clone(), new_players_tx).await?;
log::info!(
"Server is listening on {}:{}",
options.bind_address,
options.port
);
Ok(Self {
options,
clients: Clients::new(),
new_players,
waiting_chunks: WaitingChunks::default(),
chunk_subscriptions: ChunkSubscriptions::default(),
last_keepalive_time: Instant::now(),
player_count,
})
}
/// Links this server with a `Game` so that players connecting
/// to the server become part of this `Game`.
pub fn link_with_game(self, game: &mut Game, systems: &mut SystemExecutor<Game>) {
systems::register(self, game, systems);
game.add_entity_spawn_callback(entities::add_entity_components);
}
/// Gets the number of online players.
pub fn player_count(&self) -> u32 {
self.player_count.get()
}
}
/// Low-level functions, mostly used internally.
/// You may find these useful for some custom functionality.
impl Server {
/// Polls for newly connected players. Returns the IDs of the new clients.
pub fn accept_new_players(&mut self) -> Vec<ClientId> {
let mut clients = Vec::new();
for player in self.new_players.clone().try_iter() {
if let Some(old_client) = self.clients.iter().find(|x| x.uuid() == player.uuid) {
old_client.disconnect("Logged in from another location!");
}
let id = self.create_client(player);
clients.push(id);
}
clients
}
/// Removes a client.
pub fn remove_client(&mut self, id: ClientId) {
let client = self.clients.remove(id);
if let Some(client) = client {
log::debug!("Removed client for {}", client.username());
}
}
fn create_client(&mut self, player: NewPlayer) -> ClientId {
log::debug!("Creating client for {}", player.username);
let client = Client::new(player, Arc::clone(&self.options));
self.clients.insert(client)
}
/// Invokes a callback on all clients.
pub fn broadcast_with(&self, mut callback: impl FnMut(&Client)) {
for client in self.clients.iter() {
callback(client);
}
}
/// Sends a packet to all clients currently subscribed
/// to the given position. This function should be
/// used for entity updates, block updates, etc—
/// any packets that need to be sent only to nearby players.
pub fn broadcast_nearby_with(&self, position: Position, mut callback: impl FnMut(&Client)) {
for &client_id in self.chunk_subscriptions.subscriptions_for(position.chunk()) {
if let Some(client) = self.clients.get(client_id) {
callback(client);
}
}
}
pub fn broadcast_keepalive(&mut self) {
self.broadcast_with(|client| client.send_keepalive());
self.last_keepalive_time = Instant::now();
}
}