12147
|
1 |
use mio;
|
|
2 |
|
13416
|
3 |
use protocol::messages::{
|
|
4 |
HWProtocolMessage,
|
|
5 |
HWServerMessage::*
|
|
6 |
};
|
13419
|
7 |
use server::{
|
|
8 |
server::HWServer,
|
|
9 |
client::ClientId,
|
|
10 |
room::HWRoom,
|
|
11 |
actions::{Action, Action::*}
|
|
12 |
};
|
13416
|
13 |
use utils::is_name_illegal;
|
|
14 |
use std::mem::swap;
|
13423
|
15 |
use base64::{encode, decode};
|
|
16 |
|
|
17 |
#[derive(Clone)]
|
|
18 |
struct ByMsg<'a> {
|
|
19 |
messages: &'a[u8]
|
|
20 |
}
|
|
21 |
|
|
22 |
impl <'a> Iterator for ByMsg<'a> {
|
|
23 |
type Item = &'a[u8];
|
|
24 |
|
|
25 |
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
26 |
if let Some(size) = self.messages.get(0) {
|
|
27 |
let (msg, next) = self.messages.split_at(*size as usize + 1);
|
|
28 |
self.messages = next;
|
|
29 |
Some(msg)
|
|
30 |
} else {
|
|
31 |
None
|
|
32 |
}
|
|
33 |
}
|
|
34 |
}
|
|
35 |
|
|
36 |
fn by_msg(source: &Vec<u8>) -> ByMsg {
|
|
37 |
ByMsg {messages: &source[..]}
|
|
38 |
}
|
|
39 |
|
|
40 |
const VALID_MESSAGES: &[u8] =
|
|
41 |
b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
|
|
42 |
const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
|
|
43 |
|
|
44 |
fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
|
13424
|
45 |
match msg {
|
|
46 |
[size, typ, body..] => VALID_MESSAGES.contains(typ)
|
|
47 |
&& match body {
|
13423
|
48 |
[1...8, team, ..] if *typ == b'h' => team_indices.contains(team),
|
|
49 |
_ => *typ != b'h'
|
13424
|
50 |
},
|
|
51 |
_ => false
|
13423
|
52 |
}
|
|
53 |
}
|
|
54 |
|
|
55 |
fn is_msg_empty(msg: &[u8]) -> bool {
|
|
56 |
match msg {
|
|
57 |
[_, b'+', ..] => true,
|
|
58 |
_ => false
|
|
59 |
}
|
|
60 |
}
|
12147
|
61 |
|
13419
|
62 |
pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) {
|
13416
|
63 |
use protocol::messages::HWProtocolMessage::*;
|
12147
|
64 |
match message {
|
13419
|
65 |
Part(None) => server.react(client_id, vec![
|
13416
|
66 |
MoveToLobby("part".to_string())]),
|
13419
|
67 |
Part(Some(msg)) => server.react(client_id, vec![
|
13416
|
68 |
MoveToLobby(format!("part: {}", msg))]),
|
|
69 |
Chat(msg) => {
|
13419
|
70 |
let actions = {
|
|
71 |
let c = &mut server.clients[client_id];
|
|
72 |
let chat_msg = ChatMsg(c.nick.clone(), msg);
|
|
73 |
if let Some(room_id) = c.room_id {
|
|
74 |
vec![chat_msg.send_all().in_room(room_id).but_self().action()]
|
|
75 |
} else {
|
|
76 |
Vec::new()
|
|
77 |
}
|
|
78 |
};
|
|
79 |
server.react(client_id, actions);
|
13416
|
80 |
},
|
|
81 |
RoomName(new_name) => {
|
|
82 |
let actions =
|
|
83 |
if is_name_illegal(&new_name) {
|
|
84 |
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())]
|
|
85 |
} else if server.has_room(&new_name) {
|
|
86 |
vec![Warn("A room with the same name already exists.".to_string())]
|
|
87 |
} else {
|
|
88 |
let mut old_name = new_name.clone();
|
13422
|
89 |
if let (_, Some(r)) = server.client_and_room(client_id) {
|
13416
|
90 |
swap(&mut r.name, &mut old_name);
|
|
91 |
vec![SendRoomUpdate(Some(old_name))]
|
|
92 |
} else {
|
|
93 |
Vec::new()
|
|
94 |
}
|
|
95 |
};
|
13419
|
96 |
server.react(client_id, actions);
|
|
97 |
},
|
|
98 |
ToggleReady => {
|
|
99 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
100 |
let flags = if c.is_ready {
|
|
101 |
r.ready_players_number -= 1;
|
|
102 |
"-r"
|
|
103 |
} else {
|
|
104 |
r.ready_players_number += 1;
|
|
105 |
"+r"
|
|
106 |
};
|
|
107 |
c.is_ready = !c.is_ready;
|
|
108 |
vec![ClientFlags(flags.to_string(), vec![c.nick.clone()])
|
|
109 |
.send_all().in_room(r.id).action()]
|
|
110 |
} else {
|
|
111 |
Vec::new()
|
|
112 |
};
|
|
113 |
server.react(client_id, actions);
|
13416
|
114 |
}
|
13422
|
115 |
AddTeam(info) => {
|
13419
|
116 |
let mut actions = Vec::new();
|
|
117 |
if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
118 |
let room_id = r.id;
|
|
119 |
if r.teams.len() >= r.team_limit as usize {
|
|
120 |
actions.push(Warn("Too many teams!".to_string()))
|
|
121 |
} else if r.addable_hedgehogs() == 0 {
|
|
122 |
actions.push(Warn("Too many hedgehogs!".to_string()))
|
|
123 |
} else if r.find_team(|t| t.name == info.name) != None {
|
|
124 |
actions.push(Warn("There's already a team with same name in the list.".to_string()))
|
13423
|
125 |
} else if r.game_info.is_some() {
|
13419
|
126 |
actions.push(Warn("Joining not possible: Round is in progress.".to_string()))
|
|
127 |
} else {
|
|
128 |
let team = r.add_team(c.id, info);
|
|
129 |
c.teams_in_game += 1;
|
|
130 |
c.clan = Some(team.color);
|
|
131 |
actions.push(TeamAccepted(team.name.clone())
|
|
132 |
.send_self().action());
|
|
133 |
actions.push(TeamAdd(HWRoom::team_info(&c, team))
|
|
134 |
.send_all().in_room(room_id).but_self().action());
|
|
135 |
actions.push(TeamColor(team.name.clone(), team.color)
|
|
136 |
.send_all().in_room(room_id).action());
|
|
137 |
actions.push(HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
|
|
138 |
.send_all().in_room(room_id).action());
|
|
139 |
actions.push(SendRoomUpdate(None));
|
|
140 |
}
|
|
141 |
}
|
|
142 |
server.react(client_id, actions);
|
|
143 |
},
|
|
144 |
RemoveTeam(name) => {
|
|
145 |
let mut actions = Vec::new();
|
|
146 |
if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
147 |
match r.find_team_owner(&name) {
|
|
148 |
None =>
|
|
149 |
actions.push(Warn("Error: The team you tried to remove does not exist.".to_string())),
|
|
150 |
Some((id, _)) if id != client_id =>
|
|
151 |
actions.push(Warn("You can't remove a team you don't own.".to_string())),
|
|
152 |
Some((_, name)) => {
|
|
153 |
c.teams_in_game -= 1;
|
|
154 |
c.clan = r.find_team_color(c.id);
|
|
155 |
actions.push(Action::RemoveTeam(name.to_string()));
|
|
156 |
}
|
|
157 |
}
|
|
158 |
};
|
|
159 |
server.react(client_id, actions);
|
|
160 |
},
|
|
161 |
SetHedgehogsNumber(team_name, number) => {
|
|
162 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
163 |
let room_id = r.id;
|
|
164 |
let addable_hedgehogs = r.addable_hedgehogs();
|
|
165 |
if let Some((_, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
|
|
166 |
if !c.is_master {
|
|
167 |
vec![ProtocolError("You're not the room master!".to_string())]
|
|
168 |
} else if number < 1 || number > 8
|
|
169 |
|| number > addable_hedgehogs + team.hedgehogs_number {
|
|
170 |
vec![HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
|
|
171 |
.send_self().action()]
|
|
172 |
} else {
|
|
173 |
team.hedgehogs_number = number;
|
|
174 |
vec![HedgehogsNumber(team.name.clone(), number)
|
|
175 |
.send_all().in_room(room_id).but_self().action()]
|
|
176 |
}
|
|
177 |
} else {
|
|
178 |
vec![(Warn("No such team.".to_string()))]
|
|
179 |
}
|
|
180 |
} else {
|
|
181 |
Vec::new()
|
|
182 |
};
|
|
183 |
server.react(client_id, actions);
|
|
184 |
},
|
|
185 |
SetTeamColor(team_name, color) => {
|
|
186 |
let mut owner_id = None;
|
|
187 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
188 |
let room_id = r.id;
|
|
189 |
if let Some((owner, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
|
|
190 |
if !c.is_master {
|
|
191 |
vec![ProtocolError("You're not the room master!".to_string())]
|
|
192 |
} else if false {
|
|
193 |
Vec::new()
|
|
194 |
} else {
|
|
195 |
owner_id = Some(owner);
|
|
196 |
team.color = color;
|
|
197 |
vec![TeamColor(team.name.clone(), color)
|
|
198 |
.send_all().in_room(room_id).but_self().action()]
|
|
199 |
}
|
|
200 |
} else {
|
|
201 |
vec![(Warn("No such team.".to_string()))]
|
|
202 |
}
|
|
203 |
} else {
|
|
204 |
Vec::new()
|
|
205 |
};
|
|
206 |
|
|
207 |
if let Some(id) = owner_id {
|
|
208 |
server.clients[id].clan = Some(color);
|
|
209 |
}
|
|
210 |
|
|
211 |
server.react(client_id, actions);
|
13422
|
212 |
},
|
|
213 |
Cfg(cfg) => {
|
|
214 |
let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
215 |
if !c.is_master {
|
|
216 |
vec![ProtocolError("You're not the room master!".to_string())]
|
|
217 |
} else {
|
|
218 |
r.set_config(cfg.clone());
|
|
219 |
vec![cfg.into_server_msg()
|
|
220 |
.send_all().in_room(r.id).but_self().action()]
|
|
221 |
}
|
|
222 |
} else {
|
|
223 |
Vec::new()
|
|
224 |
};
|
|
225 |
server.react(client_id, actions);
|
13419
|
226 |
}
|
13423
|
227 |
StartGame => {
|
|
228 |
let actions = if let (_, Some(r)) = server.client_and_room(client_id) {
|
|
229 |
vec![StartRoomGame(r.id)]
|
|
230 |
} else {
|
|
231 |
Vec::new()
|
|
232 |
};
|
|
233 |
server.react(client_id, actions);
|
|
234 |
}
|
|
235 |
EngineMessage(em) => {
|
|
236 |
let mut actions = Vec::new();
|
|
237 |
if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
238 |
if c.teams_in_game > 0 {
|
|
239 |
let decoding = decode(&em[..]).unwrap();
|
|
240 |
let messages = by_msg(&decoding);
|
|
241 |
let valid = messages.clone().filter(|m| is_msg_valid(m, &c.team_indices));
|
13427
|
242 |
let non_empty = messages.filter(|m| !is_msg_empty(m));
|
|
243 |
let last_msg = valid.clone().scan(None, |res, msg| match msg {
|
13423
|
244 |
[_, b'+', ..] => Some(msg),
|
|
245 |
[_, typ, ..] if NON_TIMED_MESSAGES.contains(typ) => *res,
|
|
246 |
_ => None
|
13427
|
247 |
}).next().map(|s| encode(s));
|
13423
|
248 |
|
|
249 |
let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
|
|
250 |
if !em_response.is_empty() {
|
13428
|
251 |
actions.push(ForwardEngineMessage(vec![em_response])
|
13423
|
252 |
.send_all().in_room(r.id).but_self().action());
|
|
253 |
}
|
13427
|
254 |
let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
|
|
255 |
if let Some(ref mut info) = r.game_info {
|
13428
|
256 |
if (!em_log.is_empty()) {
|
|
257 |
info.msg_log.push(em_log);
|
|
258 |
}
|
13427
|
259 |
if last_msg.is_some() {
|
|
260 |
info.last_msg = last_msg;
|
|
261 |
}
|
|
262 |
}
|
13423
|
263 |
}
|
|
264 |
}
|
|
265 |
server.react(client_id, actions)
|
|
266 |
}
|
|
267 |
RoundFinished => {
|
|
268 |
let mut actions = Vec::new();
|
|
269 |
if let (c, Some(r)) = server.client_and_room(client_id) {
|
|
270 |
if c.is_in_game {
|
|
271 |
c.is_in_game = false;
|
|
272 |
actions.push(ClientFlags("-g".to_string(), vec![c.nick.clone()]).
|
|
273 |
send_all().in_room(r.id).action());
|
13426
|
274 |
if r.game_info.is_some() {
|
|
275 |
for team in r.client_teams(c.id) {
|
|
276 |
actions.push(SendTeamRemovalMessage(team.name.clone()));
|
|
277 |
}
|
13423
|
278 |
}
|
|
279 |
}
|
|
280 |
}
|
|
281 |
server.react(client_id, actions)
|
|
282 |
}
|
13419
|
283 |
_ => warn!("Unimplemented!")
|
12147
|
284 |
}
|
|
285 |
}
|