--- a/gameServer2/src/main.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/main.rs Wed Jun 27 02:34:46 2018 +0300
@@ -1,11 +1,13 @@
#![allow(unused_imports)]
#![deny(bare_trait_objects)]
#![warn(unreachable_pub)]
+#![feature(slice_patterns)]
extern crate rand;
extern crate mio;
extern crate slab;
extern crate netbuf;
+extern crate base64;
#[macro_use]
extern crate nom;
#[macro_use]
--- a/gameServer2/src/protocol/messages.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/protocol/messages.rs Wed Jun 27 02:34:46 2018 +0300
@@ -92,6 +92,9 @@
TeamColor(String, u8),
HedgehogsNumber(String, u8),
ConfigEntry(String, Vec<String>),
+ RunGame,
+ ForwardEngineMessage(String),
+ RoundFinished,
ServerMessage(String),
Warning(String),
@@ -259,6 +262,9 @@
HedgehogsNumber(name, number) => msg!["HH_NUM", name, number],
ConfigEntry(name, values) =>
construct_message(&["CFG", name], &values),
+ RunGame => msg!["RUN_GAME"],
+ ForwardEngineMessage(em) => msg!["EM", em],
+ RoundFinished => msg!["ROUND_FINISHED"],
ChatMsg(nick, msg) => msg!["CHAT", nick, msg],
ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
Warning(msg) => msg!["WARNING", msg],
--- a/gameServer2/src/protocol/parser.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/protocol/parser.rs Wed Jun 27 02:34:46 2018 +0300
@@ -36,7 +36,7 @@
| do_parse!(tag!("GET_SERVER_VAR") >> (GetServerVar))
| do_parse!(tag!("TOGGLE_READY") >> (ToggleReady))
| do_parse!(tag!("START_GAME") >> (StartGame))
- | do_parse!(tag!("ROUNDFINISHED") >> (RoundFinished))
+ | do_parse!(tag!("ROUNDFINISHED") >> m: opt_param >> (RoundFinished))
| do_parse!(tag!("TOGGLE_RESTRICT_JOINS") >> (ToggleRestrictJoin))
| do_parse!(tag!("TOGGLE_RESTRICT_TEAMS") >> (ToggleRestrictTeams))
| do_parse!(tag!("TOGGLE_REGISTERED_ONLY") >> (ToggleRegisteredOnly))
--- a/gameServer2/src/server/actions.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/server/actions.rs Wed Jun 27 02:34:46 2018 +0300
@@ -1,9 +1,10 @@
use std::{
- io, io::Write
+ io, io::Write,
+ iter::once
};
use super::{
server::HWServer,
- room::RoomId,
+ room::{RoomId, GameInfo},
client::{ClientId, HWClient},
room::HWRoom,
handlers
@@ -13,10 +14,11 @@
HWServerMessage,
HWServerMessage::*
};
+use utils::to_engine_msg;
pub enum Destination {
ToSelf,
- ToAll {
+ ToAll {
room_id: Option<RoomId>,
protocol: Option<u32>,
skip_self: bool
@@ -90,6 +92,9 @@
RemoveTeam(String),
RemoveClientTeams,
SendRoomUpdate(Option<String>),
+ StartRoomGame(RoomId),
+ SendTeamRemovalMessage(String),
+ FinishRoomGame(RoomId),
Warn(String),
ProtocolError(String)
}
@@ -309,8 +314,59 @@
Vec::new()
};
server.react(client_id, actions);
+ },
+ StartRoomGame(room_id) => {
+ let actions = {
+ let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server.clients.iter()
+ .map(|(id, c)| (id, c.nick.clone())).unzip();
+ let room = &mut server.rooms[room_id];
+
+ if !room.has_multiple_clans() {
+ vec![Warn("The game can't be started with less than two clans!".to_string())]
+ } else if room.game_info.is_some() {
+ vec![Warn("The game is already in progress".to_string())]
+ } else {
+ room.game_info = Some(GameInfo {
+ teams_in_game: room.teams.len() as u8
+ });
+ for id in room_clients {
+ let c = &mut server.clients[id];
+ c.is_in_game = true;
+ c.team_indices = room.client_team_indices(c.id);
+ }
+ vec![RunGame.send_all().in_room(room.id).action(),
+ SendRoomUpdate(None),
+ ClientFlags("+g".to_string(), room_nicks)
+ .send_all().in_room(room.id).action()]
+ }
+ };
+ server.react(client_id, actions);
}
-
+ SendTeamRemovalMessage(team_name) => {
+ let mut actions = Vec::new();
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ if let Some(ref mut info) = r.game_info {
+ let msg = once(b'F').chain(team_name.bytes());
+ actions.push(ForwardEngineMessage(to_engine_msg(msg)).
+ send_all().in_room(r.id).but_self().action());
+ info.teams_in_game -= 1;
+ if info.teams_in_game == 0 {
+ actions.push(FinishRoomGame(r.id));
+ }
+ }
+ }
+ server.react(client_id, actions);
+ }
+ FinishRoomGame(room_id) => {
+ let actions = {
+ let r = &mut server.rooms[room_id];
+ r.game_info = None;
+ r.ready_players_number = 0;
+ vec![SendRoomUpdate(None),
+ RoundFinished.send_all().in_room(r.id).action()]
+ };
+ server.react(client_id, actions);
+ }
Warn(msg) => {
run_action(server, client_id, Warning(msg).send_self().action());
}
--- a/gameServer2/src/server/client.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/server/client.rs Wed Jun 27 02:34:46 2018 +0300
@@ -7,7 +7,9 @@
pub protocol_number: u32,
pub is_master: bool,
pub is_ready: bool,
+ pub is_in_game: bool,
pub teams_in_game: u8,
+ pub team_indices: Vec<u8>,
pub clan: Option<u8>,
pub is_joined_mid_game: bool,
}
@@ -21,7 +23,9 @@
protocol_number: 0,
is_master: false,
is_ready: false,
+ is_in_game: false,
teams_in_game: 0,
+ team_indices: Vec::new(),
clan: None,
is_joined_mid_game: false,
}
--- a/gameServer2/src/server/handlers/inroom.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/server/handlers/inroom.rs Wed Jun 27 02:34:46 2018 +0300
@@ -12,6 +12,53 @@
};
use utils::is_name_illegal;
use std::mem::swap;
+use base64::{encode, decode};
+
+#[derive(Clone)]
+struct ByMsg<'a> {
+ messages: &'a[u8]
+}
+
+impl <'a> Iterator for ByMsg<'a> {
+ type Item = &'a[u8];
+
+ fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+ if let Some(size) = self.messages.get(0) {
+ let (msg, next) = self.messages.split_at(*size as usize + 1);
+ self.messages = next;
+ Some(msg)
+ } else {
+ None
+ }
+ }
+}
+
+fn by_msg(source: &Vec<u8>) -> ByMsg {
+ ByMsg {messages: &source[..]}
+}
+
+const VALID_MESSAGES: &[u8] =
+ b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
+const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
+
+fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
+ if let [size, typ, body..] = msg {
+ VALID_MESSAGES.contains(typ) &&
+ match body {
+ [1...8, team, ..] if *typ == b'h' => team_indices.contains(team),
+ _ => *typ != b'h'
+ }
+ } else {
+ false
+ }
+}
+
+fn is_msg_empty(msg: &[u8]) -> bool {
+ match msg {
+ [_, b'+', ..] => true,
+ _ => false
+ }
+}
pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) {
use protocol::messages::HWProtocolMessage::*;
@@ -76,7 +123,7 @@
actions.push(Warn("Too many hedgehogs!".to_string()))
} else if r.find_team(|t| t.name == info.name) != None {
actions.push(Warn("There's already a team with same name in the list.".to_string()))
- } else if r.game_info != None {
+ } else if r.game_info.is_some() {
actions.push(Warn("Joining not possible: Round is in progress.".to_string()))
} else {
let team = r.add_team(c.id, info);
@@ -178,6 +225,51 @@
};
server.react(client_id, actions);
}
+ StartGame => {
+ let actions = if let (_, Some(r)) = server.client_and_room(client_id) {
+ vec![StartRoomGame(r.id)]
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, actions);
+ }
+ EngineMessage(em) => {
+ let mut actions = Vec::new();
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ if c.teams_in_game > 0 {
+ let decoding = decode(&em[..]).unwrap();
+ let messages = by_msg(&decoding);
+ let valid = messages.clone().filter(|m| is_msg_valid(m, &c.team_indices));
+ let _non_empty = messages.filter(|m| !is_msg_empty(m));
+ let _last_valid_timed_msg = valid.clone().scan(None, |res, msg| match msg {
+ [_, b'+', ..] => Some(msg),
+ [_, typ, ..] if NON_TIMED_MESSAGES.contains(typ) => *res,
+ _ => None
+ }).next();
+
+ let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
+ if !em_response.is_empty() {
+ actions.push(ForwardEngineMessage(em_response)
+ .send_all().in_room(r.id).but_self().action());
+ }
+ }
+ }
+ server.react(client_id, actions)
+ }
+ RoundFinished => {
+ let mut actions = Vec::new();
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ if c.is_in_game {
+ c.is_in_game = false;
+ actions.push(ClientFlags("-g".to_string(), vec![c.nick.clone()]).
+ send_all().in_room(r.id).action());
+ for team in r.client_teams(c.id) {
+ actions.push(SendTeamRemovalMessage(team.name.clone()));
+ }
+ }
+ }
+ server.react(client_id, actions)
+ }
_ => warn!("Unimplemented!")
}
}
--- a/gameServer2/src/server/room.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/server/room.rs Wed Jun 27 02:34:46 2018 +0300
@@ -51,6 +51,10 @@
}
}
+pub struct GameInfo {
+ pub teams_in_game: u8
+}
+
pub struct HWRoom {
pub id: RoomId,
pub master_id: Option<ClientId>,
@@ -64,7 +68,7 @@
pub ready_players_number: u8,
pub teams: Vec<(ClientId, TeamInfo)>,
config: RoomConfig,
- pub game_info: Option<()>
+ pub game_info: Option<GameInfo>
}
impl HWRoom {
@@ -127,6 +131,12 @@
self.teams.iter().filter(move |(id, _)| *id == client_id).map(|(_, t)| t)
}
+ pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
+ self.teams.iter().enumerate()
+ .filter(move |(_, (id, _))| *id == client_id)
+ .map(|(i, _)| i as u8).collect()
+ }
+
pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
self.teams.iter().find(|(_, t)| t.name == team_name)
.map(|(id, t)| (*id, &t.name[..]))
@@ -136,6 +146,11 @@
self.client_teams(owner_id).nth(0).map(|t| t.color)
}
+ pub fn has_multiple_clans(&self) -> bool {
+ self.teams.iter().min_by_key(|(_, t)| t.color) !=
+ self.teams.iter().max_by_key(|(_, t)| t.color)
+ }
+
pub fn set_config(&mut self, cfg: GameCfg) {
let c = &mut self.config;
match cfg {
--- a/gameServer2/src/utils.rs Tue Jun 26 23:22:38 2018 +0300
+++ b/gameServer2/src/utils.rs Wed Jun 27 02:34:46 2018 +0300
@@ -1,4 +1,6 @@
+use std::iter::Iterator;
use mio;
+use base64::{encode};
pub const PROTOCOL_VERSION : u32 = 3;
pub const SERVER: mio::Token = mio::Token(1000000000 + 0);
@@ -9,4 +11,13 @@
name.chars().any(|c|
"$()*+?[]^{|}\x7F".contains(c) ||
'\x00' <= c && c <= '\x1F')
+}
+
+pub fn to_engine_msg<T>(msg: T) -> String
+ where T: Iterator<Item = u8> + Clone
+{
+ let mut tmp = Vec::new();
+ tmp.push(msg.clone().count() as u8);
+ tmp.extend(msg);
+ encode(&tmp)
}
\ No newline at end of file