forked from feather-rs/feather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
150 lines (129 loc) · 4.52 KB
/
lib.rs
File metadata and controls
150 lines (129 loc) · 4.52 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
148
149
150
#![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 [`register`] 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() {
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());
}
}
/// Allocates a `NetworkId` for an entity.
pub fn create_network_id(&mut self) -> NetworkId {
NetworkId::new()
}
fn create_client(&mut self, player: NewPlayer) -> ClientId {
log::debug!("Creating client for {}", player.username);
let network_id = self.create_network_id();
let client = Client::new(player, Arc::clone(&self.options), network_id);
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();
}
}