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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
use specs;
use specs::{WriteStorage, Fetch, FetchMut};
use slog::Logger;
use super::{
CellDweller,
RecvMessageQueue,
SendMessageQueue,
CellDwellerMessage,
SendMessage,
RemoveBlockMessage,
};
use Spatial;
use grid::PosInOwningRoot;
use globe::Globe;
use net::{
EntityIds,
NodeResource,
Destination,
Transport,
};
pub struct RecvSystem {
log: Logger,
}
impl RecvSystem {
pub fn new(
world: &mut specs::World,
parent_log: &Logger,
) -> RecvSystem {
use ::AutoResource;
RecvMessageQueue::ensure(world);
RecvSystem {
log: parent_log.new(o!()),
}
}
}
impl<'a> specs::System<'a> for RecvSystem {
type SystemData = (
WriteStorage<'a, Globe>,
WriteStorage<'a, CellDweller>,
WriteStorage<'a, Spatial>,
FetchMut<'a, RecvMessageQueue>,
FetchMut<'a, SendMessageQueue>,
Fetch<'a, EntityIds>,
Fetch<'a, NodeResource>,
);
fn run(&mut self, data: Self::SystemData) {
let (
mut globes,
mut cell_dwellers,
mut spatials,
mut recv_message_queue,
mut send_message_queue,
entity_ids,
node_resource,
) = data;
// Slurp all inbound messages.
while let Some(message) = recv_message_queue.queue.pop_front() {
match message.game_message {
CellDwellerMessage::SetPos(set_pos_message) => {
// Look up the entity from its global ID.
let cell_dweller_entity = match entity_ids.mapping.get(&set_pos_message.entity_id) {
Some(ent) => ent,
// We probably just don't know about it yet.
None => {
// TODO: demote to trace
info!(self.log, "Heard about cell dweller we don't know about yet"; "entity_id" => set_pos_message.entity_id);
continue;
},
};
let cd = cell_dwellers.get_mut(*cell_dweller_entity).expect(
"Missing CellDweller",
);
let spatial = spatials.get_mut(*cell_dweller_entity).expect(
"Missing Spatial",
);
// TODO: validate that they're allowed to move this cell dweller.
// TODO: demote to trace
debug!(self.log, "Moving cell dweller because of received network message"; "message" => format!("{:?}", set_pos_message));
cd.set_cell_transform(
set_pos_message.new_pos,
set_pos_message.new_dir,
set_pos_message.new_last_turn_bias,
);
// Update real-space coordinates if necessary.
// TODO: do this in a separate system; it needs to be done before
// things are rendered, but there might be other effects like gravity,
// enemies shunting the cell dweller around, etc. that happen
// after control.
if cd.is_real_space_transform_dirty() {
spatial.set_local_transform(cd.get_real_transform_and_mark_as_clean());
}
// Inform all peers that don't yet know about this action.
// TODO: do we need some kind of pattern for an action,
// where it's got rules for:
// - how to turn it into a request if we're not the server.
// - how to action it if we are the server.
// - how to action it if we are a client. (Maybe it's just
// a different kind of message in that case.)
// - how to forward it on if we're the server and we just
// acted on it.
if node_resource.is_master {
send_message_queue.queue.push_back(
SendMessage {
destination: Destination::EveryoneElseExcept(message.source),
game_message: CellDwellerMessage::SetPos(set_pos_message),
transport: Transport::UDP,
}
)
}
},
CellDwellerMessage::TryPickUpBlock(try_pick_up_block_message) => {
// TODO: validate that we are the server.
// Look up the entity from its global ID.
let cell_dweller_entity = match entity_ids.mapping.get(&try_pick_up_block_message.cd_entity_id) {
Some(ent) => ent,
// We probably just don't know about it yet.
None => {
// TODO: demote to trace
info!(self.log, "Heard about cell dweller we don't know about yet"; "entity_id" => try_pick_up_block_message.cd_entity_id);
continue;
},
};
let cd = cell_dwellers.get_mut(*cell_dweller_entity).expect(
"Missing CellDweller",
);
// Get the associated globe, complaining loudly if we fail.
let globe_entity = match cd.globe_entity {
Some(globe_entity) => globe_entity,
None => {
warn!(
self.log,
"There was no associated globe entity or it wasn't actually a Globe! Can't proceed!"
);
return;
}
};
let globe = match globes.get_mut(globe_entity) {
Some(globe) => globe,
None => {
warn!(
self.log,
"The globe associated with this CellDweller is not alive! Can't proceed!"
);
return;
}
};
// TODO: validate that peer is allowed to remove the block.
// TODO: handle their source position and target pickup spot.
// Initially just trust the client is honest.
let maybe_cell_info = super::mining::pick_up_if_possible(cd, globe);
if let Some((new_pos_in_owning_root, cell)) = maybe_cell_info {
debug!(self.log, "Removed a block because a peer asked"; "pos" => format!("{:?}", new_pos_in_owning_root), "cell" => format!("{:?}", cell));
// Tell everyone else what happened.
//
// TODO: this needs to be a much more sophisticated kind of messaging
// about chunk content changing over time, not sending it to clients
// that clearly shouldn't need to care, allowing clients to
// ignore messages they receive if they decide they don't care,
// then catch up later, etc.
//
// But for now, just say "this block is gone"!
let remove_block_message = RemoveBlockMessage {
// TODO: identify the globe. But for that we'd first need
// the server to inform clients about the globe it created.
// For now we'll just use the first globe we find.
// globe_entity_id: ......,
pos: new_pos_in_owning_root.into(),
};
send_message_queue.queue.push_back(
SendMessage {
destination: Destination::EveryoneElse,
game_message: CellDwellerMessage::RemoveBlock(remove_block_message),
transport: Transport::TCP,
}
);
}
},
CellDwellerMessage::RemoveBlock(remove_block_message) => {
// For now just find the first globe, and assume that's
// the one we're supposed to be working with.
use specs::Join;
let globe = (&mut globes).join().next().expect("Should've been at least one globe.");
// TODO: validate that position makes sense. Don't want the client
// to be able to punk us.
let pos_in_owning_root =
PosInOwningRoot::new(remove_block_message.pos, globe.spec().root_resolution);
let removed_cell = super::mining::remove_block(globe, pos_in_owning_root);
debug!(self.log, "Removed a block master told me to"; "pos" => format!("{:?}", remove_block_message.pos), "cell" => format!("{:?}", removed_cell));
},
}
}
}
}