-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathserver.rs
More file actions
158 lines (138 loc) · 4.98 KB
/
Copy pathserver.rs
File metadata and controls
158 lines (138 loc) · 4.98 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
151
152
153
154
155
156
157
158
use std::{sync::Arc, time::Instant};
use flume::Receiver;
use common::Game;
use libcraft::Position;
use quill::WorldId;
use vane::SystemExecutor;
use crate::{
chunk_subscriptions::{ChunkPositionWithWorld, ChunkSubscriptions},
initial_handler::NewPlayer,
listener::Listener,
player_count::PlayerCount,
Client, ClientId, Clients, Options,
};
/// 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 {
pub(crate) options: Arc<Options>,
pub(crate) clients: Clients,
pub(crate) new_players: Receiver<NewPlayer>,
pub(crate) chunk_subscriptions: ChunkSubscriptions,
pub(crate) last_keepalive_time: Instant,
pub(crate) 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,
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>) {
crate::systems::register(self, game, systems);
game.add_entity_spawn_callback(crate::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_mut().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,
world: WorldId,
position: Position,
mut callback: impl FnMut(&Client),
) {
for &client_id in self
.chunk_subscriptions
.subscriptions_for(ChunkPositionWithWorld::new(world, position.chunk()))
{
if let Some(client) = self.clients.get(client_id) {
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_mut(
&mut self,
world: WorldId,
position: Position,
mut callback: impl FnMut(&mut Client),
) {
for &client_id in self
.chunk_subscriptions
.subscriptions_for(ChunkPositionWithWorld::new(world, position.chunk()))
{
if let Some(client) = self.clients.get_mut(client_id) {
callback(client);
}
}
}
pub fn broadcast_keepalive(&mut self) {
self.broadcast_with(|client| client.send_keepalive());
self.last_keepalive_time = Instant::now();
}
}