author | alfadur |
Sat, 07 Jul 2018 20:22:31 +0300 | |
changeset 13478 | d79795acaa73 |
parent 13477 | f748a72432f2 |
child 13480 | fb37745c5bca |
permissions | -rw-r--r-- |
12147 | 1 |
use mio; |
2 |
||
13416 | 3 |
use protocol::messages::{ |
4 |
HWProtocolMessage, |
|
13478 | 5 |
HWServerMessage::*, |
6 |
server_chat |
|
13416 | 7 |
}; |
13419 | 8 |
use server::{ |
13478 | 9 |
coretypes::{ClientId, Voting, VoteType}, |
13419 | 10 |
server::HWServer, |
11 |
room::HWRoom, |
|
12 |
actions::{Action, Action::*} |
|
13 |
}; |
|
13416 | 14 |
use utils::is_name_illegal; |
15 |
use std::mem::swap; |
|
13423 | 16 |
use base64::{encode, decode}; |
13445
d3c86ade3d4d
Send the rnd reply to the room only.
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13444
diff
changeset
|
17 |
use super::common::rnd_action; |
13423 | 18 |
|
19 |
#[derive(Clone)] |
|
20 |
struct ByMsg<'a> { |
|
21 |
messages: &'a[u8] |
|
22 |
} |
|
23 |
||
24 |
impl <'a> Iterator for ByMsg<'a> { |
|
25 |
type Item = &'a[u8]; |
|
26 |
||
27 |
fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
|
28 |
if let Some(size) = self.messages.get(0) { |
|
29 |
let (msg, next) = self.messages.split_at(*size as usize + 1); |
|
30 |
self.messages = next; |
|
31 |
Some(msg) |
|
32 |
} else { |
|
33 |
None |
|
34 |
} |
|
35 |
} |
|
36 |
} |
|
37 |
||
38 |
fn by_msg(source: &Vec<u8>) -> ByMsg { |
|
39 |
ByMsg {messages: &source[..]} |
|
40 |
} |
|
41 |
||
42 |
const VALID_MESSAGES: &[u8] = |
|
43 |
b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A"; |
|
44 |
const NON_TIMED_MESSAGES: &[u8] = b"M#hb"; |
|
45 |
||
13429 | 46 |
#[cfg(canhazslicepatterns)] |
13423 | 47 |
fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool { |
13424 | 48 |
match msg { |
49 |
[size, typ, body..] => VALID_MESSAGES.contains(typ) |
|
50 |
&& match body { |
|
13423 | 51 |
[1...8, team, ..] if *typ == b'h' => team_indices.contains(team), |
52 |
_ => *typ != b'h' |
|
13424 | 53 |
}, |
54 |
_ => false |
|
13423 | 55 |
} |
56 |
} |
|
57 |
||
13429 | 58 |
fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool { |
59 |
if let Some(typ) = msg.get(1) { |
|
60 |
VALID_MESSAGES.contains(typ) |
|
61 |
} else { |
|
62 |
false |
|
63 |
} |
|
64 |
} |
|
65 |
||
13423 | 66 |
fn is_msg_empty(msg: &[u8]) -> bool { |
13429 | 67 |
msg.get(1).filter(|t| **t == b'+').is_some() |
13423 | 68 |
} |
12147 | 69 |
|
13443 | 70 |
fn is_msg_timed(msg: &[u8]) -> bool { |
71 |
msg.get(1).filter(|t| !NON_TIMED_MESSAGES.contains(t)).is_some() |
|
72 |
} |
|
73 |
||
13419 | 74 |
pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) { |
13416 | 75 |
use protocol::messages::HWProtocolMessage::*; |
12147 | 76 |
match message { |
13419 | 77 |
Part(None) => server.react(client_id, vec![ |
13416 | 78 |
MoveToLobby("part".to_string())]), |
13419 | 79 |
Part(Some(msg)) => server.react(client_id, vec![ |
13416 | 80 |
MoveToLobby(format!("part: {}", msg))]), |
81 |
Chat(msg) => { |
|
13419 | 82 |
let actions = { |
83 |
let c = &mut server.clients[client_id]; |
|
13444
914f9b970f4d
Implement server-side logic for Rnd
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13443
diff
changeset
|
84 |
let chat_msg = ChatMsg {nick: c.nick.clone(), msg: msg}; |
13419 | 85 |
if let Some(room_id) = c.room_id { |
86 |
vec![chat_msg.send_all().in_room(room_id).but_self().action()] |
|
87 |
} else { |
|
88 |
Vec::new() |
|
89 |
} |
|
90 |
}; |
|
91 |
server.react(client_id, actions); |
|
13416 | 92 |
}, |
13477 | 93 |
Fix => { |
94 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
95 |
if c.is_admin { r.is_fixed = true } |
|
96 |
} |
|
97 |
} |
|
98 |
Unfix => { |
|
99 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
100 |
if c.is_admin { r.is_fixed = false } |
|
101 |
} |
|
102 |
} |
|
103 |
Greeting(text) => { |
|
104 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
105 |
if c.is_admin || c.is_master && !r.is_fixed { |
|
106 |
r.greeting = text |
|
107 |
} |
|
108 |
} |
|
109 |
} |
|
13416 | 110 |
RoomName(new_name) => { |
111 |
let actions = |
|
112 |
if is_name_illegal(&new_name) { |
|
113 |
vec![Warn("Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}".to_string())] |
|
13477 | 114 |
} else if server.room(client_id).map(|r| r.is_fixed).unwrap_or(false) { |
115 |
vec![Warn("Access denied.".to_string())] |
|
13416 | 116 |
} else if server.has_room(&new_name) { |
117 |
vec![Warn("A room with the same name already exists.".to_string())] |
|
118 |
} else { |
|
119 |
let mut old_name = new_name.clone(); |
|
13422 | 120 |
if let (_, Some(r)) = server.client_and_room(client_id) { |
13416 | 121 |
swap(&mut r.name, &mut old_name); |
122 |
vec![SendRoomUpdate(Some(old_name))] |
|
123 |
} else { |
|
124 |
Vec::new() |
|
125 |
} |
|
126 |
}; |
|
13419 | 127 |
server.react(client_id, actions); |
128 |
}, |
|
129 |
ToggleReady => { |
|
130 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
131 |
let flags = if c.is_ready { |
|
132 |
r.ready_players_number -= 1; |
|
133 |
"-r" |
|
134 |
} else { |
|
135 |
r.ready_players_number += 1; |
|
136 |
"+r" |
|
137 |
}; |
|
138 |
c.is_ready = !c.is_ready; |
|
13477 | 139 |
let mut v = |
140 |
vec![ClientFlags(flags.to_string(), vec![c.nick.clone()]) |
|
141 |
.send_all().in_room(r.id).action()]; |
|
142 |
if r.is_fixed && r.ready_players_number as u32 == r.players_number { |
|
143 |
v.push(StartRoomGame(r.id)) |
|
144 |
} |
|
145 |
v |
|
13419 | 146 |
} else { |
147 |
Vec::new() |
|
148 |
}; |
|
149 |
server.react(client_id, actions); |
|
13416 | 150 |
} |
13422 | 151 |
AddTeam(info) => { |
13419 | 152 |
let mut actions = Vec::new(); |
153 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
154 |
let room_id = r.id; |
|
155 |
if r.teams.len() >= r.team_limit as usize { |
|
156 |
actions.push(Warn("Too many teams!".to_string())) |
|
157 |
} else if r.addable_hedgehogs() == 0 { |
|
158 |
actions.push(Warn("Too many hedgehogs!".to_string())) |
|
159 |
} else if r.find_team(|t| t.name == info.name) != None { |
|
160 |
actions.push(Warn("There's already a team with same name in the list.".to_string())) |
|
13423 | 161 |
} else if r.game_info.is_some() { |
13419 | 162 |
actions.push(Warn("Joining not possible: Round is in progress.".to_string())) |
163 |
} else { |
|
164 |
let team = r.add_team(c.id, info); |
|
165 |
c.teams_in_game += 1; |
|
166 |
c.clan = Some(team.color); |
|
167 |
actions.push(TeamAccepted(team.name.clone()) |
|
168 |
.send_self().action()); |
|
169 |
actions.push(TeamAdd(HWRoom::team_info(&c, team)) |
|
170 |
.send_all().in_room(room_id).but_self().action()); |
|
171 |
actions.push(TeamColor(team.name.clone(), team.color) |
|
172 |
.send_all().in_room(room_id).action()); |
|
173 |
actions.push(HedgehogsNumber(team.name.clone(), team.hedgehogs_number) |
|
174 |
.send_all().in_room(room_id).action()); |
|
175 |
actions.push(SendRoomUpdate(None)); |
|
176 |
} |
|
177 |
} |
|
178 |
server.react(client_id, actions); |
|
179 |
}, |
|
180 |
RemoveTeam(name) => { |
|
181 |
let mut actions = Vec::new(); |
|
182 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
183 |
match r.find_team_owner(&name) { |
|
184 |
None => |
|
185 |
actions.push(Warn("Error: The team you tried to remove does not exist.".to_string())), |
|
186 |
Some((id, _)) if id != client_id => |
|
187 |
actions.push(Warn("You can't remove a team you don't own.".to_string())), |
|
188 |
Some((_, name)) => { |
|
189 |
c.teams_in_game -= 1; |
|
190 |
c.clan = r.find_team_color(c.id); |
|
191 |
actions.push(Action::RemoveTeam(name.to_string())); |
|
192 |
} |
|
193 |
} |
|
194 |
}; |
|
195 |
server.react(client_id, actions); |
|
196 |
}, |
|
197 |
SetHedgehogsNumber(team_name, number) => { |
|
198 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
199 |
let room_id = r.id; |
|
200 |
let addable_hedgehogs = r.addable_hedgehogs(); |
|
201 |
if let Some((_, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) { |
|
202 |
if !c.is_master { |
|
203 |
vec![ProtocolError("You're not the room master!".to_string())] |
|
204 |
} else if number < 1 || number > 8 |
|
205 |
|| number > addable_hedgehogs + team.hedgehogs_number { |
|
206 |
vec![HedgehogsNumber(team.name.clone(), team.hedgehogs_number) |
|
207 |
.send_self().action()] |
|
208 |
} else { |
|
209 |
team.hedgehogs_number = number; |
|
210 |
vec![HedgehogsNumber(team.name.clone(), number) |
|
211 |
.send_all().in_room(room_id).but_self().action()] |
|
212 |
} |
|
213 |
} else { |
|
214 |
vec![(Warn("No such team.".to_string()))] |
|
215 |
} |
|
216 |
} else { |
|
217 |
Vec::new() |
|
218 |
}; |
|
219 |
server.react(client_id, actions); |
|
220 |
}, |
|
221 |
SetTeamColor(team_name, color) => { |
|
222 |
let mut owner_id = None; |
|
223 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
224 |
let room_id = r.id; |
|
225 |
if let Some((owner, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) { |
|
226 |
if !c.is_master { |
|
227 |
vec![ProtocolError("You're not the room master!".to_string())] |
|
228 |
} else if false { |
|
229 |
Vec::new() |
|
230 |
} else { |
|
231 |
owner_id = Some(owner); |
|
232 |
team.color = color; |
|
233 |
vec![TeamColor(team.name.clone(), color) |
|
234 |
.send_all().in_room(room_id).but_self().action()] |
|
235 |
} |
|
236 |
} else { |
|
237 |
vec![(Warn("No such team.".to_string()))] |
|
238 |
} |
|
239 |
} else { |
|
240 |
Vec::new() |
|
241 |
}; |
|
242 |
||
243 |
if let Some(id) = owner_id { |
|
244 |
server.clients[id].clan = Some(color); |
|
245 |
} |
|
246 |
||
247 |
server.react(client_id, actions); |
|
13422 | 248 |
}, |
249 |
Cfg(cfg) => { |
|
250 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
13477 | 251 |
if r.is_fixed { |
252 |
vec![Warn("Access denied.".to_string())] |
|
253 |
} else if !c.is_master { |
|
13422 | 254 |
vec![ProtocolError("You're not the room master!".to_string())] |
255 |
} else { |
|
13439 | 256 |
let v = vec![cfg.to_server_msg() |
257 |
.send_all().in_room(r.id).but_self().action()]; |
|
258 |
r.set_config(cfg); |
|
259 |
v |
|
13422 | 260 |
} |
261 |
} else { |
|
262 |
Vec::new() |
|
263 |
}; |
|
264 |
server.react(client_id, actions); |
|
13419 | 265 |
} |
13478 | 266 |
CallVote(None) => { |
267 |
server.react(client_id, vec![ |
|
268 |
server_chat("Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs <number>") |
|
269 |
.send_self().action()]) |
|
270 |
} |
|
271 |
CallVote(Some(kind)) => { |
|
272 |
let (room_id, is_in_game) = server.room(client_id) |
|
273 |
.map(|r| (r.id, r.game_info.is_some())).unwrap(); |
|
274 |
let error = match &kind { |
|
275 |
VoteType::Kick(nick) => { |
|
276 |
if server.find_client(&nick).filter(|c| c.room_id == Some(room_id)).is_some() { |
|
277 |
None |
|
278 |
} else { |
|
279 |
Some("/callvote kick: No such user!") |
|
280 |
} |
|
281 |
}, |
|
282 |
VoteType::Map(None) => { |
|
283 |
Some("/callvote map: Not implemented") |
|
284 |
}, |
|
285 |
VoteType::Map(Some(name)) => { |
|
286 |
Some("/callvote map: Not implemented") |
|
287 |
}, |
|
288 |
VoteType::Pause => { |
|
289 |
if is_in_game { |
|
290 |
None |
|
291 |
} else { |
|
292 |
Some("/callvote pause: No game in progress!") |
|
293 |
} |
|
294 |
}, |
|
295 |
VoteType::NewSeed => { |
|
296 |
None |
|
297 |
}, |
|
298 |
VoteType::HedgehogsPerTeam(number) => { |
|
299 |
match number { |
|
300 |
1...8 => None, |
|
301 |
_ => Some("/callvote hedgehogs: Specify number from 1 to 8.") |
|
302 |
} |
|
303 |
}, |
|
304 |
}; |
|
305 |
match error { |
|
306 |
None => { |
|
307 |
let voting = Voting::new(kind, server.room_clients(client_id)); |
|
308 |
server.room(client_id).unwrap().voting = Some(voting); |
|
309 |
server.react(client_id, vec![ |
|
310 |
server_chat("New voting started: ") |
|
311 |
.send_all().in_room(room_id).action(), |
|
312 |
AddVote{ vote: true, is_forced: false}]); |
|
313 |
} |
|
314 |
Some(msg) => { |
|
315 |
server.react(client_id, vec![ |
|
316 |
server_chat(msg).send_self().action()]) |
|
317 |
} |
|
318 |
} |
|
319 |
} |
|
320 |
Vote(vote) => { |
|
321 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
322 |
vec![AddVote{ vote, is_forced: false }] |
|
323 |
} else { |
|
324 |
Vec::new() |
|
325 |
}; |
|
326 |
server.react(client_id, actions); |
|
327 |
} |
|
328 |
ForceVote(vote) => { |
|
329 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) { |
|
330 |
vec![AddVote{ vote, is_forced: c.is_admin} ] |
|
331 |
} else { |
|
332 |
Vec::new() |
|
333 |
}; |
|
334 |
server.react(client_id, actions); |
|
335 |
} |
|
13423 | 336 |
StartGame => { |
337 |
let actions = if let (_, Some(r)) = server.client_and_room(client_id) { |
|
338 |
vec![StartRoomGame(r.id)] |
|
339 |
} else { |
|
340 |
Vec::new() |
|
341 |
}; |
|
342 |
server.react(client_id, actions); |
|
343 |
} |
|
344 |
EngineMessage(em) => { |
|
345 |
let mut actions = Vec::new(); |
|
346 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
347 |
if c.teams_in_game > 0 { |
|
348 |
let decoding = decode(&em[..]).unwrap(); |
|
349 |
let messages = by_msg(&decoding); |
|
13443 | 350 |
let valid = messages.filter(|m| is_msg_valid(m, &c.team_indices)); |
351 |
let non_empty = valid.clone().filter(|m| !is_msg_empty(m)); |
|
352 |
let sync_msg = valid.clone().filter(|m| is_msg_timed(m)) |
|
353 |
.last().map(|m| if is_msg_empty(m) {Some(encode(m))} else {None}); |
|
13423 | 354 |
|
355 |
let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>()); |
|
356 |
if !em_response.is_empty() { |
|
13428 | 357 |
actions.push(ForwardEngineMessage(vec![em_response]) |
13423 | 358 |
.send_all().in_room(r.id).but_self().action()); |
359 |
} |
|
13427 | 360 |
let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>()); |
361 |
if let Some(ref mut info) = r.game_info { |
|
13429 | 362 |
if !em_log.is_empty() { |
13428 | 363 |
info.msg_log.push(em_log); |
364 |
} |
|
13443 | 365 |
if let Some(msg) = sync_msg { |
366 |
info.sync_msg = msg; |
|
13427 | 367 |
} |
368 |
} |
|
13423 | 369 |
} |
370 |
} |
|
371 |
server.react(client_id, actions) |
|
372 |
} |
|
373 |
RoundFinished => { |
|
374 |
let mut actions = Vec::new(); |
|
375 |
if let (c, Some(r)) = server.client_and_room(client_id) { |
|
376 |
if c.is_in_game { |
|
377 |
c.is_in_game = false; |
|
378 |
actions.push(ClientFlags("-g".to_string(), vec![c.nick.clone()]). |
|
379 |
send_all().in_room(r.id).action()); |
|
13426 | 380 |
if r.game_info.is_some() { |
381 |
for team in r.client_teams(c.id) { |
|
382 |
actions.push(SendTeamRemovalMessage(team.name.clone())); |
|
383 |
} |
|
13423 | 384 |
} |
385 |
} |
|
386 |
} |
|
387 |
server.react(client_id, actions) |
|
13444
914f9b970f4d
Implement server-side logic for Rnd
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13443
diff
changeset
|
388 |
}, |
13445
d3c86ade3d4d
Send the rnd reply to the room only.
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13444
diff
changeset
|
389 |
Rnd(v) => { |
d3c86ade3d4d
Send the rnd reply to the room only.
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13444
diff
changeset
|
390 |
let actions = rnd_action(v, server.room(client_id)); |
d3c86ade3d4d
Send the rnd reply to the room only.
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13444
diff
changeset
|
391 |
server.react(client_id, actions) |
d3c86ade3d4d
Send the rnd reply to the room only.
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13444
diff
changeset
|
392 |
}, |
13419 | 393 |
_ => warn!("Unimplemented!") |
12147 | 394 |
} |
395 |
} |