Implement game config messages
authoralfadur
Tue, 26 Jun 2018 23:22:38 +0300
changeset 13427 5fb27f94fc3b
parent 13426 d1368c776a4f
child 13428 87a6cad20c90
Implement game config messages
gameServer2/src/protocol/messages.rs
gameServer2/src/protocol/parser.rs
gameServer2/src/server/actions.rs
gameServer2/src/server/coretypes.rs
gameServer2/src/server/handlers/inroom.rs
gameServer2/src/server/room.rs
gameServer2/src/server/server.rs
--- a/gameServer2/src/protocol/messages.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/protocol/messages.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -91,6 +91,7 @@
     TeamAccepted(String),
     TeamColor(String, u8),
     HedgehogsNumber(String, u8),
+    ConfigEntry(String, Vec<String>),
 
     ServerMessage(String),
     Warning(String),
@@ -99,6 +100,33 @@
     Unreachable,
 }
 
+impl GameCfg {
+    pub fn into_server_msg(self) -> HWServerMessage {
+        use self::HWServerMessage::ConfigEntry;
+        use server::coretypes::GameCfg::*;
+        match self {
+            FeatureSize(s) => ConfigEntry("FEATURE_SIZE".to_string(), vec![s.to_string()]),
+            MapType(t) => ConfigEntry("MAP".to_string(), vec![t.to_string()]),
+            MapGenerator(g) => ConfigEntry("MAPGEN".to_string(), vec![g.to_string()]),
+            MazeSize(s) => ConfigEntry("MAZE_SIZE".to_string(), vec![s.to_string()]),
+            Seed(s) => ConfigEntry("SEED".to_string(), vec![s.to_string()]),
+            Template(t) => ConfigEntry("TEMPLATE".to_string(), vec![t.to_string()]),
+
+            Ammo(n, None) => ConfigEntry("AMMO".to_string(), vec![n.to_string()]),
+            Ammo(n, Some(s)) => ConfigEntry("AMMO".to_string(), vec![n.to_string(), s.to_string()]),
+            Scheme(n, None) => ConfigEntry("SCHEME".to_string(), vec![n.to_string()]),
+            Scheme(n, Some(s)) => ConfigEntry("SCHEME".to_string(), {
+                let mut v = vec![n.to_string()];
+                v.extend(s.into_iter());
+                v
+            }),
+            Script(s) => ConfigEntry("SCRIPT".to_string(), vec![s.to_string()]),
+            Theme(t) => ConfigEntry("THEME".to_string(), vec![t.to_string()]),
+            DrawnMap(m) => ConfigEntry("DRAWNMAP".to_string(), vec![m.to_string()])
+        }
+    }
+}
+
 impl<'a> HWProtocolMessage {
     pub fn to_raw_protocol(&self) -> String {
         use self::HWProtocolMessage::*;
@@ -229,6 +257,8 @@
             TeamAccepted(name) => msg!["TEAM_ACCEPTED", name],
             TeamColor(name, color) => msg!["TEAM_COLOR", name, color],
             HedgehogsNumber(name, number) => msg!["HH_NUM", name, number],
+            ConfigEntry(name, values) =>
+                construct_message(&["CFG", name], &values),
             ChatMsg(nick, msg) => msg!["CHAT", nick, msg],
             ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
             Warning(msg) => msg!["WARNING", msg],
--- a/gameServer2/src/protocol/parser.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/protocol/parser.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -9,7 +9,7 @@
     test::gen_proto_msg
 };
 use server::coretypes::{
-    HedgehogInfo, TeamInfo
+    HedgehogInfo, TeamInfo, GameCfg
 };
 
 named!(end_of_message, tag!("\n\n"));
@@ -139,6 +139,45 @@
                     (BanNick(n, r, t)))
 ));
 
+named!(cfg_message<&[u8], HWProtocolMessage>, preceded!(tag!("CFG\n"), map!(alt!(
+      do_parse!(tag!("THEME")    >> eol >>
+                name: a_line     >>
+                (GameCfg::Theme(name)))
+    | do_parse!(tag!("SCRIPT")   >> eol >>
+                name: a_line     >>
+                (GameCfg::Script(name)))
+    | do_parse!(tag!("AMMO")     >> eol >>
+                name: a_line     >>
+                value: opt_param >>
+                (GameCfg::Ammo(name, value)))
+    | do_parse!(tag!("SCHEME")   >> eol >>
+                name: a_line     >> eol >>
+                values: separated_list!(eol, a_line) >>
+                (GameCfg::Scheme(name,
+                    if values.is_empty() {None} else {Some(values)})))
+    | do_parse!(tag!("FEATURE_SIZE") >> eol >>
+                value: u32_line    >>
+                (GameCfg::FeatureSize(value)))
+    | do_parse!(tag!("MAP")      >> eol >>
+                value: a_line    >>
+                (GameCfg::MapType(value)))
+    | do_parse!(tag!("MAPGEN")   >> eol >>
+                value: u32_line  >>
+                (GameCfg::MapGenerator(value)))
+    | do_parse!(tag!("MAZE_SIZE") >> eol >>
+                value: u32_line   >>
+                (GameCfg::MazeSize(value)))
+    | do_parse!(tag!("SEED")     >> eol >>
+                value: a_line    >>
+                (GameCfg::Seed(value)))
+    | do_parse!(tag!("TEMPLATE") >> eol >>
+                value: u32_line  >>
+                (GameCfg::Template(value)))
+    | do_parse!(tag!("DRAWNMAP") >> eol >>
+                value: a_line    >>
+                (GameCfg::DrawnMap(value)))
+), Cfg)));
+
 named!(malformed_message<&[u8], HWProtocolMessage>,
     do_parse!(separated_list!(eol, a_line) >> (Malformed)));
 
@@ -151,6 +190,7 @@
         | one_param_message
         | cmd_message
         | complex_message
+        | cfg_message
         ), end_of_message
     )
     | terminated!(malformed_message, end_of_message)
--- a/gameServer2/src/server/actions.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/server/actions.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -211,9 +211,18 @@
                 }
                 let flags_msg = ClientFlags("+i".to_string(), vec![c.nick.clone()]);
 
-                vec![RoomJoined(vec![c.nick.clone()]).send_all().in_room(room_id).action(),
-                     flags_msg.send_all().action(),
-                     SendRoomUpdate(None)]
+                let mut v = vec![
+                    RoomJoined(vec![c.nick.clone()]).send_all().in_room(room_id).action(),
+                    flags_msg.send_all().action(),
+                    SendRoomUpdate(None)];
+                if !c.is_master {
+                    v.push(ConfigEntry("FULLMAPCONFIG".to_string(), r.map_config())
+                        .send_self().action());
+                    for cfg in r.game_config().into_iter() {
+                        v.push(cfg.into_server_msg().send_self().action());
+                    }
+                }
+                v
             };
             server.react(client_id, actions);
         },
@@ -274,7 +283,7 @@
             server.react(client_id, actions);
         }
         RemoveTeam(name) => {
-            let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+            let actions = if let (_, Some(r)) = server.client_and_room(client_id) {
                 r.remove_team(&name);
                 vec![TeamRemove(name).send_all().in_room(r.id).action(),
                      SendRoomUpdate(None)]
--- a/gameServer2/src/server/coretypes.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/server/coretypes.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -7,7 +7,18 @@
 
 #[derive(PartialEq, Eq, Clone, Debug)]
 pub enum GameCfg {
+    FeatureSize(u32),
+    MapType(String),
+    MapGenerator(u32),
+    MazeSize(u32),
+    Seed(String),
+    Template(u32),
 
+    Ammo(String, Option<String>),
+    Scheme(String, Option<Vec<String>>),
+    Script(String),
+    Theme(String),
+    DrawnMap(String)
 }
 
 #[derive(PartialEq, Eq, Clone, Debug)]
--- a/gameServer2/src/server/handlers/inroom.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/server/handlers/inroom.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -40,7 +40,7 @@
                     vec![Warn("A room with the same name already exists.".to_string())]
                 } else {
                     let mut old_name = new_name.clone();
-                    if let (c, Some(r)) = server.client_and_room(client_id) {
+                    if let (_, Some(r)) = server.client_and_room(client_id) {
                         swap(&mut r.name, &mut old_name);
                         vec![SendRoomUpdate(Some(old_name))]
                     } else {
@@ -66,7 +66,7 @@
             };
             server.react(client_id, actions);
         }
-        AddTeam(mut info) => {
+        AddTeam(info) => {
             let mut actions = Vec::new();
             if let (c, Some(r)) = server.client_and_room(client_id) {
                 let room_id = r.id;
@@ -163,6 +163,20 @@
             }
 
             server.react(client_id, actions);
+        },
+        Cfg(cfg) => {
+            let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+                if !c.is_master {
+                    vec![ProtocolError("You're not the room master!".to_string())]
+                } else {
+                    r.set_config(cfg.clone());
+                    vec![cfg.into_server_msg()
+                        .send_all().in_room(r.id).but_self().action()]
+                }
+            } else {
+                Vec::new()
+            };
+            server.react(client_id, actions);
         }
         _ => warn!("Unimplemented!")
     }
--- a/gameServer2/src/server/room.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/server/room.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -1,12 +1,56 @@
-use std::iter;
+use std::{iter};
 use server::{
-    coretypes::{TeamInfo, GameCfg},
+    coretypes::{TeamInfo, GameCfg, GameCfg::*},
     client::{ClientId, HWClient}
 };
 
 const MAX_HEDGEHOGS_IN_ROOM: u8 = 48;
 pub type RoomId = usize;
 
+struct Ammo {
+    name: String,
+    settings: Option<String>
+}
+
+struct Scheme {
+    name: String,
+    settings: Option<Vec<String>>
+}
+
+struct RoomConfig {
+    feature_size: u32,
+    map_type: String,
+    map_generator: u32,
+    maze_size: u32,
+    seed: String,
+    template: u32,
+
+    ammo: Ammo,
+    scheme: Scheme,
+    script: String,
+    theme: String,
+    drawn_map: Option<String>
+}
+
+impl RoomConfig {
+    fn new() -> RoomConfig {
+        RoomConfig {
+            feature_size: 12,
+            map_type: "+rnd+".to_string(),
+            map_generator: 0,
+            maze_size: 0,
+            seed: "seed".to_string(),
+            template: 0,
+
+            ammo: Ammo {name: "Default".to_string(), settings: None },
+            scheme: Scheme {name: "Default".to_string(), settings: None },
+            script: "Normal".to_string(),
+            theme: "\u{1f994}".to_string(),
+            drawn_map: None
+        }
+    }
+}
+
 pub struct HWRoom {
     pub id: RoomId,
     pub master_id: Option<ClientId>,
@@ -19,6 +63,7 @@
     pub team_limit: u8,
     pub ready_players_number: u8,
     pub teams: Vec<(ClientId, TeamInfo)>,
+    config: RoomConfig,
     pub game_info: Option<()>
 }
 
@@ -35,6 +80,7 @@
             team_limit: 8,
             ready_players_number: 0,
             teams: Vec::new(),
+            config: RoomConfig::new(),
             game_info: None
         }
     }
@@ -90,21 +136,61 @@
         self.client_teams(owner_id).nth(0).map(|t| t.color)
     }
 
+    pub fn set_config(&mut self, cfg: GameCfg) {
+        let c = &mut self.config;
+        match cfg {
+            FeatureSize(s) => c.feature_size = s,
+            MapType(t) => c.map_type = t,
+            MapGenerator(g) => c.map_generator = g,
+            MazeSize(s) => c.maze_size = s,
+            Seed(s) => c.seed = s,
+            Template(t) => c.template = t,
+
+            Ammo(n, s) => c.ammo = Ammo {name: n, settings: s},
+            Scheme(n, s) => c.scheme = Scheme {name: n, settings: s},
+            Script(s) => c.script = s,
+            Theme(t) => c.theme = t,
+            DrawnMap(m) => c.drawn_map = Some(m)
+        };
+    }
+
     pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
         let flags = "-".to_string();
+        let c = &self.config;
         vec![
             flags,
             self.name.clone(),
             self.players_number.to_string(),
             self.teams.len().to_string(),
-            master.map_or("?", |c| &c.nick).to_string(),
-            "Normal".to_string(),
-            "Default".to_string(),
-            "Default".to_string(),
-            "Default".to_string(),
+            master.map_or("[]", |c| &c.nick).to_string(),
+            c.map_type.to_string(),
+            c.script.to_string(),
+            c.scheme.name.to_string(),
+            c.ammo.name.to_string()
         ]
     }
 
+    pub fn map_config(&self) -> Vec<String> {
+        let c = &self.config;
+        vec![c.feature_size.to_string(), c.map_type.to_string(),
+             c.map_generator.to_string(), c.maze_size.to_string(),
+             c.seed.to_string(), c.template.to_string()]
+    }
+
+    pub fn game_config(&self) -> Vec<GameCfg> {
+        use server::coretypes::GameCfg::*;
+        let c = &self.config;
+        let mut v = vec![
+            Ammo(c.ammo.name.to_string(), c.ammo.settings.clone()),
+            Scheme(c.scheme.name.to_string(), c.scheme.settings.clone()),
+            Script(c.script.to_string()),
+            Theme(c.theme.to_string())];
+        if let Some(ref m) = c.drawn_map {
+            v.push(DrawnMap(m.to_string()))
+        }
+        v
+    }
+
     pub fn team_info(owner: &HWClient, team: &TeamInfo) -> Vec<String> {
         let mut info = vec![
             team.name.clone(),
--- a/gameServer2/src/server/server.rs	Sun Jun 24 12:09:31 2018 -0400
+++ b/gameServer2/src/server/server.rs	Tue Jun 26 23:22:38 2018 +0300
@@ -69,8 +69,7 @@
             Destination::ToAll {protocol: Some(proto), ..} =>
                 self.protocol_clients(proto),
             Destination::ToAll {..} =>
-                self.clients.iter().map(|(id, _)| id).collect::<Vec<_>>(),
-            _ => Vec::new()
+                self.clients.iter().map(|(id, _)| id).collect::<Vec<_>>()
         };
         if let Destination::ToAll {skip_self: true, ..} = destination {
             if let Some(index) = ids.iter().position(|id| *id == client_id) {