diff --git a/secert-hitler/src/controller/gamemaster/gameevent.rs b/secert-hitler/src/controller/gamemaster/gameevent.rs new file mode 100644 index 0000000..d51c43b --- /dev/null +++ b/secert-hitler/src/controller/gamemaster/gameevent.rs @@ -0,0 +1,162 @@ +use super::super::super::model::state::event::Event; +use json; + +#[derive(Clone, Debug)] +pub struct GameEvent { + d: json::JsonValue, +} + +#[derive(PartialEq, Eq, Debug)] +pub enum GameEventType { + Null, + GameStart, + RoleSet, + ElectionPend, + ElectionSet, + VoteSet, + CardPend, + CardPick, + PolicySet, + SpecialInspect, + SpecialSelect, + SpecialKill, + SpecialPeek, + GameStop, +} + +impl GameEventType { + fn from_string(s: String) -> GameEventType { + match s.as_ref() { + "Null" => return GameEventType::Null, + "GameStart" => return GameEventType::GameStart, + "RoleSet" => return GameEventType::RoleSet, + "ElectionPend" => return GameEventType::ElectionPend, + "ElectionSet" => return GameEventType::ElectionSet, + "VoteSet" => return GameEventType::VoteSet, + "CardPend" => return GameEventType::CardPend, + "CardPick" => return GameEventType::CardPick, + "PolicySet" => return GameEventType::PolicySet, + "SpecialInspect" => return GameEventType::SpecialInspect, + "SpecialSelect" => return GameEventType::SpecialSelect, + "SpecialKill" => return GameEventType::SpecialKill, + "SpecialPeek" => return GameEventType::SpecialPeek, + "GameStop" => return GameEventType::GameStop, + _ => return GameEventType::Null, + } + } +} + +impl GameEvent { + pub fn new(body: String) -> GameEvent { + let top_level = json::parse(&body); + if top_level.is_err() { + return GameEvent{d: json::Null}; + } + let top_level = top_level.unwrap(); + if !top_level["body"].is_string() { + return GameEvent{d: json::Null}; + } + GameEvent{ + d: json::parse(&top_level["body"].as_str().unwrap()).unwrap_or(json::Null), + } + } + + pub fn mode(&self) -> GameEventType { + if self.d.is_null() { + return GameEventType::Null; + } + if !self.d["GameEventType"].is_string() { + return GameEventType::Null; + } + let s = self.d["GameEventType"].as_str().unwrap().to_string(); + GameEventType::from_string(s.to_string()) + } + + pub fn sources(&self) -> Vec { + self.str_vec("sources") + } + + pub fn targets(&self) -> Vec { + self.str_vec("targets") + } + + pub fn params(&self) -> Vec { + self.str_vec("params") + } + + fn str_vec(&self, key: &str) -> Vec { + let mut out = vec![]; + if self.d[key].is_string() { + out.push(self.d[key].as_str().unwrap().to_string()); + return out; + } + if self.d[key].is_array() { + let iter = self.d[key].members(); + for i in iter { + if !i.is_string() { + return vec![]; + } + out.push(i.as_str().unwrap().to_string()); + } + return out; + } + out + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new() { + let ge = GameEvent::new("\"a\"".to_string()); + assert!(ge.d.is_null()); + + let ge = GameEvent::new("a".to_string()); + assert!(ge.d.is_null()); + assert!(ge.mode() == GameEventType::Null); + } + + #[test] + fn type_from_string() { + assert!(GameEventType::from_string("Null".to_string()) == GameEventType::Null); + assert!(GameEventType::from_string("GameStart".to_string()) == GameEventType::GameStart); + assert!(GameEventType::from_string("RoleSet".to_string()) == GameEventType::RoleSet); + assert!(GameEventType::from_string("ElectionPend".to_string()) == GameEventType::ElectionPend); + assert!(GameEventType::from_string("ElectionSet".to_string()) == GameEventType::ElectionSet); + assert!(GameEventType::from_string("VoteSet".to_string()) == GameEventType::VoteSet); + assert!(GameEventType::from_string("CardPend".to_string()) == GameEventType::CardPend); + assert!(GameEventType::from_string("CardPick".to_string()) == GameEventType::CardPick); + assert!(GameEventType::from_string("PolicySet".to_string()) == GameEventType::PolicySet); + assert!(GameEventType::from_string("SpecialInspect".to_string()) == GameEventType::SpecialInspect); + assert!(GameEventType::from_string("SpecialSelect".to_string()) == GameEventType::SpecialSelect); + assert!(GameEventType::from_string("SpecialKill".to_string()) == GameEventType::SpecialKill); + assert!(GameEventType::from_string("SpecialPeek".to_string()) == GameEventType::SpecialPeek); + assert!(GameEventType::from_string("GameStop".to_string()) == GameEventType::GameStop); + } + + #[test] + fn sources() { + let ge = GameEvent::new(r#"{"body": "{\"sources\": [\"a\", \"b\"]}"}"#.to_string()); + assert!(ge.sources() == vec!["a", "b"]); + } + + #[test] + fn targets() { + let ge = GameEvent::new(r#"{"body": "{\"targets\": [\"a\", \"b\"]}"}"#.to_string()); + assert!(ge.targets() == vec!["a", "b"]); + } + + #[test] + fn params() { + let ge = GameEvent::new(r#"{"body": "{\"params\": [\"a\", \"b\"]}"}"#.to_string()); + assert!(ge.params() == vec!["a", "b"]); + + let ge = GameEvent::new(r#"{"body": "{\"params\": []}"}"#.to_string()); + assert!(ge.params().len() == 0); + + let ge = GameEvent::new(r#"{"body": "{}"}"#.to_string()); + assert!(ge.params().len() == 0); + } +} diff --git a/secert-hitler/src/controller/gamemaster/gamemaster.rs b/secert-hitler/src/controller/gamemaster/gamemaster.rs index 2ab4484..7d214fa 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster.rs @@ -1,5 +1,9 @@ use super::super::super::model::state::room::Room; use super::lobby::Lobby; +use super::gameevent::GameEvent; +use super::gameevent::GameEventType; +use std::thread; +use std::time; pub struct GameMaster { room: Box, @@ -21,18 +25,24 @@ impl GameMaster { } fn run_lobby(&mut self) -> Result { - println!(". run lobby"); - loop { + while !self.lobby.locked { let rollback = self.room.since(); let events = self.room.sync(); - println!(". rollback: {:?}", rollback); + println!("top of loop: since={:?}, events={:?}", self.room.since(), events); for e in &events { - println!("e: {:?}", e); + let ge = GameEvent::new(e.body.clone()); + println!(" ge.mode = {:?}", ge.mode()); + if ge.mode() == GameEventType::GameStart { + println!(" should return"); + self.room.rollback(e.since.clone()); + self.lobby.lock(); + return Ok("ok".to_string()); + } + self.lobby.eat(e.clone()); } - self.room.rollback(rollback); - break + thread::sleep(time::Duration::new(1, 0)); } - Err("not impl".to_string()) + Ok("ok".to_string()) // todo: on lobby too small, return err and do not lock } fn run_game_setup(&mut self) -> Result { @@ -54,7 +64,6 @@ mod tests { #[test] fn new_mockroom() { let _ = GameMaster::new(Box::new(MockRoom::create())); - println!("made"); } #[test] @@ -78,27 +87,33 @@ mod tests { #[test] fn run_lobby() { let mut mrs = MockRooms::new(); - let mut r1 = mrs.create(); + let r1 = mrs.create(); let room_id = r1.room_id(); let mut gm = GameMaster::new(r1); let mut r2 = mrs.join(room_id).unwrap(); - r2.send(r#"{ + r2.send(format!(r#"{{ "msgtype": "m.text", - "body": "" - }"#); - gm.run_lobby(); - assert!(gm.lobby.players.len() == 1, "players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync()); + "body": "{{\"GameEventType\": \"GameStart\"}}" + }}"#)).unwrap(); // TODO mark end of new players + gm.run_lobby(); // todo assert is_ok + assert!(gm.lobby.players.len() == 2, "first run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync()); + let mut players1 = vec![]; + for k in gm.lobby.players.keys() { + players1.push(k.clone()); + } + gm.run_lobby(); // todo assert is_ok + assert!(gm.lobby.players.len() == 2, "secnd run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync()); + let players2 = gm.lobby.players.keys(); + assert!(format!("{:?}", players1) == format!("{:?}", players2)); } #[test] fn run_game_setup_fail() { - let gm = GameMaster::new(Box::new(MockRoom::create())); - panic!("not impl"); + assert!(false, "not impl"); } #[test] fn run_game_fail() { - let gm = GameMaster::new(Box::new(MockRoom::create())); - panic!("not impl"); + assert!(false, "not impl"); } } diff --git a/secert-hitler/src/controller/gamemaster/lobby.rs b/secert-hitler/src/controller/gamemaster/lobby.rs index 9c25896..73e7b70 100644 --- a/secert-hitler/src/controller/gamemaster/lobby.rs +++ b/secert-hitler/src/controller/gamemaster/lobby.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; pub struct Lobby { pub players: HashMap, - locked: bool, + pub locked: bool, } impl Lobby { diff --git a/secert-hitler/src/controller/gamemaster/mod.rs b/secert-hitler/src/controller/gamemaster/mod.rs index 1d8e735..bfefc32 100644 --- a/secert-hitler/src/controller/gamemaster/mod.rs +++ b/secert-hitler/src/controller/gamemaster/mod.rs @@ -1,4 +1,5 @@ -// pub mod gamemaster; +pub mod gamemaster; pub mod player; pub mod role; pub mod lobby; +pub mod gameevent; diff --git a/secert-hitler/src/model/state/mockroom.rs b/secert-hitler/src/model/state/mockroom.rs index 1c6f6e8..e00957c 100644 --- a/secert-hitler/src/model/state/mockroom.rs +++ b/secert-hitler/src/model/state/mockroom.rs @@ -159,7 +159,7 @@ mod tests { let mut r = _dummy(); r.sync(); let mut sinces = vec![]; - for i in 0..10 { + for _ in 0..10 { sinces.push(r.send("0".to_string()).ok().unwrap()); sinces.push(r.send("0".to_string()).ok().unwrap()); r.sync(); @@ -172,7 +172,7 @@ mod tests { let mut r = _dummy(); r.sync(); let mut sinces = vec![]; - for i in 0..10 { + for _ in 0..10 { sinces.push(r.send("0".to_string()).ok().unwrap()); r.sync(); assert!(r.since == sinces[sinces.len()-1]); diff --git a/secert-hitler/src/model/state/mockrooms.rs b/secert-hitler/src/model/state/mockrooms.rs index 2167441..17d4404 100644 --- a/secert-hitler/src/model/state/mockrooms.rs +++ b/secert-hitler/src/model/state/mockrooms.rs @@ -31,7 +31,7 @@ impl Rooms for MockRooms { r.send(format!(r#"{{ "displayname": "{}", "membership": "join" - }}"#, rands())); + }}"#, rands())).unwrap(); return Ok(Box::new(r)); } }