-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathview.rs
More file actions
112 lines (99 loc) · 3.43 KB
/
view.rs
File metadata and controls
112 lines (99 loc) · 3.43 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
//! Sends and unloads entities and chunks for a client.
//!
//! The entities and chunks visible to each client are
//! determined based on the player's [`common::view::View`].
use ahash::AHashMap;
use base::{ChunkPosition, Position};
use common::{
events::{ChunkLoadEvent, ViewUpdateEvent},
Game,
};
use ecs::{Entity, SysResult, SystemExecutor};
use crate::{Client, ClientId, Server};
pub fn register(_game: &mut Game, systems: &mut SystemExecutor<Game>) {
systems
.group::<Server>()
.add_system(send_new_chunks)
.add_system(send_loaded_chunks);
}
/// Stores the players waiting on chunks that are currently being loaded.
#[derive(Default)]
pub struct WaitingChunks(AHashMap<ChunkPosition, Vec<Entity>>);
impl WaitingChunks {
pub fn drain_players_waiting_for(&mut self, chunk: ChunkPosition) -> Vec<Entity> {
self.0.remove(&chunk).unwrap_or_default()
}
pub fn insert(&mut self, player: Entity, chunk: ChunkPosition) {
self.0.entry(chunk).or_default().push(player);
}
}
fn send_new_chunks(game: &mut Game, server: &mut Server) -> SysResult {
for (player, (&client_id, event, &position)) in game
.ecs
.query::<(&ClientId, &ViewUpdateEvent, &Position)>()
.iter()
{
// As ecs removes the client one tick after it gets removed here, it can
// happen that a client is still listed in the ecs but actually removed here so
// we need to check if the client is actually still there.
if let Some(client) = server.clients.get(client_id) {
client.update_own_chunk(event.new_view.center());
update_chunks(
game,
player,
client,
event,
position,
&mut server.waiting_chunks,
)?;
}
}
Ok(())
}
fn update_chunks(
game: &Game,
player: Entity,
client: &Client,
event: &ViewUpdateEvent,
position: Position,
waiting_chunks: &mut WaitingChunks,
) -> SysResult {
// Send chunks that are in the new view but not the old view.
for &pos in &event.new_chunks {
if let Some(chunk) = game.world.chunk_map().chunk_handle_at(pos) {
client.send_chunk(&chunk);
} else {
waiting_chunks.insert(player, pos);
}
}
// Unsend the chunks that are in the old view but not the new view.
for &pos in &event.old_chunks {
client.unload_chunk(pos);
}
spawn_client_if_needed(client, position);
Ok(())
}
/// Sends newly loaded chunks to players currently
/// waiting for those chunks to load.
fn send_loaded_chunks(game: &mut Game, server: &mut Server) -> SysResult {
for (_, event) in game.ecs.query::<&ChunkLoadEvent>().iter() {
for player in server
.waiting_chunks
.drain_players_waiting_for(event.position)
{
if let Ok(client_id) = game.ecs.get::<ClientId>(player) {
if let Some(client) = server.clients.get(*client_id) {
client.send_chunk(&event.chunk);
spawn_client_if_needed(client, *game.ecs.get::<Position>(player)?);
}
}
}
}
Ok(())
}
fn spawn_client_if_needed(client: &Client, pos: Position) {
if !client.knows_own_position() && client.known_chunks() >= 9 * 9 {
log::debug!("Sent all chunks to {}; now spawning", client.username());
client.update_own_position(pos);
}
}