room joining reuqires sender

master^2
Bel LaPointe 2020-05-07 07:46:43 -06:00
parent 3e9ce3edff
commit 3859f29a75
9 changed files with 177 additions and 54 deletions

View File

@ -61,7 +61,7 @@ impl GameEventType {
params: [], params: [],
}.dump(), }.dump(),
}; };
GameEvent::new(Event{sender: "<generated>".to_string(), body: d.dump(), since: "".to_string()}) GameEvent::new(Event{sender: "__gm__".to_string(), body: d.dump(), since: "".to_string()})
} }
} }
@ -75,9 +75,15 @@ impl GameEvent {
if !top_level["body"].is_string() { if !top_level["body"].is_string() {
return GameEvent{d: json::Null, sender: event.sender.clone()}; return GameEvent{d: json::Null, sender: event.sender.clone()};
} }
let body = top_level["body"].as_str().unwrap();
let d = json::parse(&body).unwrap_or(json::Null);
let sender = event.sender.clone();
if d.is_object() && d.has_key("sender") && d["sender"].is_string() {
// sender = d["sender"].as_str().unwrap();
}
GameEvent{ GameEvent{
d: json::parse(&top_level["body"].as_str().unwrap()).unwrap_or(json::Null), d: d,
sender: event.sender.clone(), sender: sender,
} }
} }

View File

@ -6,6 +6,7 @@ use super::super::role::Role;
use log::debug; use log::debug;
use json; use json;
use std::collections::HashMap;
impl GameMaster { impl GameMaster {
pub fn game_is_over(&mut self) -> Result<GameEvent, Role> { pub fn game_is_over(&mut self) -> Result<GameEvent, Role> {
@ -30,6 +31,7 @@ impl GameMaster {
pub fn game_election(&mut self) -> Result<GameEvent, Role> { pub fn game_election(&mut self) -> Result<GameEvent, Role> {
let mut ge = GameEventType::ElectionPend.build(); let mut ge = GameEventType::ElectionPend.build();
let president_candidate = self.candidate_presidents.pop().unwrap(); let president_candidate = self.candidate_presidents.pop().unwrap();
self.president = Some(president_candidate.clone());
ge.d["targets"] = json::array![president_candidate.clone()]; ge.d["targets"] = json::array![president_candidate.clone()];
ge.d["params"] = json::array!["president"]; ge.d["params"] = json::array!["president"];
if self.room.send(ge.serialize()).is_err() { if self.room.send(ge.serialize()).is_err() {
@ -51,11 +53,55 @@ impl GameMaster {
debug!("invalid chancellor candidates found in election set"); debug!("invalid chancellor candidates found in election set");
return self.game_election(); return self.game_election();
} }
self.chancellor = Some(chancellor_candidate.clone());
Ok(chancellor_candidate_event) Ok(chancellor_candidate_event)
} }
pub fn game_election_vote(&mut self) -> Result<GameEvent, Role> { pub fn game_election_vote(&mut self) -> Result<GameEvent, Role> {
Err(Role::Null) let mut votes: HashMap<String, bool> = HashMap::new();
while votes.len() < self.players().len() {
debug!("votes: {:?}", votes);
let events = self.scrape_until_gameeventtype(GameEventType::VoteSet).unwrap();
debug!("scrape until vote set found {:?}", events);
if events.len() > 0 {
let ge = GameEvent::new(events.last().unwrap().clone());
let sources = ge.sources();
if sources.len() > 0 {
let player = sources.first().unwrap();
if self.player(player.clone()).is_some() {
let params = ge.params();
if params.len() > 0 {
votes.insert(player.clone(), params.first().unwrap() == "y");
}
}
}
}
}
debug!("game election vote yielded: {:?}", votes);
let mut ge = GameEventType::VoteSet.build();
ge.d["sources"] = json::array!["__gm__"];
let mut yays = 0;
for (player, vote) in &votes {
if *vote {
yays += 1;
}
}
let gm_vote = yays > self.players().len() / 2;
ge.d["params"] = json::array![format!("{:?}", gm_vote)];
for (player, vote) in &votes {
ge.d["sources"].push(player.clone());
ge.d["params"].push(format!("{:?}", vote));
}
self.room.send(ge.serialize());
if gm_vote {
return Ok(GameEventType::Null.build()); // todo how to emit continue?
} else {
self.president = None;
self.chancellor = None;
return Ok(GameEventType::VoteFailed.build());
}
} }
pub fn game_policy_select_random(&mut self) -> Result<GameEvent, Role> { pub fn game_policy_select_random(&mut self) -> Result<GameEvent, Role> {
@ -100,13 +146,13 @@ mod tests {
fn dummy() -> GameMaster { fn dummy() -> GameMaster {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r = mrs.create(); let r = mrs.create("__gm__".to_string());
let room_id = r.room_id().clone(); let room_id = r.room_id().clone();
let mut gm = GameMaster::new(r); let mut gm = GameMaster::new(r);
for _ in 0..config::MIN_PLAYERS-2 { for i in 0..config::MIN_PLAYERS-2 {
assert!(mrs.join(room_id.clone()).is_ok()); assert!(mrs.join(i.to_string(), room_id.clone()).is_ok());
} }
mrs.join(room_id).unwrap().send(format!(r#"{{ mrs.join((config::MIN_PLAYERS-1).to_string(), room_id).unwrap().send(format!(r#"{{
"msgtype": "m.text", "msgtype": "m.text",
"body": "{{\"GameEventType\": \"GameStart\"}}" "body": "{{\"GameEventType\": \"GameStart\"}}"
}}"#)).unwrap(); }}"#)).unwrap();
@ -235,4 +281,65 @@ mod tests {
let election_pends = gm.room.sync(); let election_pends = gm.room.sync();
assert!(election_pends.len() == 6, "election_pends: {:?}", election_pends); assert!(election_pends.len() == 6, "election_pends: {:?}", election_pends);
} }
#[test]
fn game_election_vote() {
let mut gm = dummy();
gm.lobby.players = HashMap::new();
debug!("sending a null event");
let ge = GameEventType::Null.build();
assert!(ge.mode() == GameEventType::Null);
assert!(gm.room.send(ge.serialize()).is_ok());
debug!("sent: {}", ge.serialize());
debug!("sending a valid vote from a valid player");
let mut ge = GameEventType::VoteSet.build();
assert!(ge.mode() == GameEventType::VoteSet);
ge.d["sources"] = json::array!["1"];
ge.d["params"] = json::array!["n"];
assert!(gm.room.send(ge.serialize()).is_ok());
debug!("sent: {}", ge.serialize());
gm.lobby.players.insert("1".to_string(), Player::new("1".to_string()));
debug!("sending a valid repeat vote from a valid player");
let mut ge = GameEventType::VoteSet.build();
assert!(ge.mode() == GameEventType::VoteSet);
ge.d["sources"] = json::array!["1"];
ge.d["params"] = json::array!["y"];
assert!(gm.room.send(ge.serialize()).is_ok());
debug!("sent: {}", ge.serialize());
debug!("sending an invalid vote from a valid player");
let mut ge = GameEventType::VoteSet.build();
assert!(ge.mode() == GameEventType::VoteSet);
ge.d["sources"] = json::array![];
ge.d["params"] = json::array!["n"];
assert!(gm.room.send(ge.serialize()).is_ok());
debug!("sent: {}", ge.serialize());
gm.lobby.players.insert("2".to_string(), Player::new("2".to_string()));
debug!("correcting an invalid vote from a valid player");
let mut ge = GameEventType::VoteSet.build();
assert!(ge.mode() == GameEventType::VoteSet);
ge.d["sources"] = json::array!["2"];
ge.d["params"] = json::array!["y"];
assert!(gm.room.send(ge.serialize()).is_ok());
debug!("sent: {}", ge.serialize());
let ge = gm.game_election_vote();
debug!("game election vote result: {:?}", ge);
assert!(ge.is_ok());
let events = gm.room.sync();
assert!(events.len() == 1);
let e = events.last().unwrap().clone();
let ge = GameEvent::new(e.clone());
assert!(ge.sender == "__gm__", "post-game election vote ge: {:?} from {:?}", ge, e);
assert!(ge.mode() == GameEventType::VoteSet);
assert!(ge.sources()[0] == "__gm__");
assert!(ge.params()[0] == "true");
assert!(ge.params()[1] == "true");
assert!(ge.params()[2] == "true");
}
} }

View File

@ -51,14 +51,18 @@ impl GameMaster {
pub fn run_game(&mut self) -> Result<GameEvent, Role> { pub fn run_game(&mut self) -> Result<GameEvent, Role> {
loop { loop {
self.game_is_over()?; let ge = self.game_is_over()?;
self.game_election()?; self.room.send(ge.serialize());
self.game_is_over()?; let ge = self.game_election()?;
self.room.send(ge.serialize());
let ge = self.game_is_over()?;
self.room.send(ge.serialize());
let p = match self.game_election_vote().unwrap_or(GameEventType::GameStop.build()).mode() { let p = match self.game_election_vote().unwrap_or(GameEventType::GameStop.build()).mode() {
GameEventType::VoteFailed => self.game_policy_select_random(), GameEventType::VoteFailed => self.game_policy_select_random(),
GameEventType::GameStop => self.game_is_over(), GameEventType::GameStop => self.game_is_over(),
_ => self.game_policy_select(), _ => self.game_policy_select(),
}?; }?;
self.room.send(p.serialize());
if p.mode() != GameEventType::PolicySet { if p.mode() != GameEventType::PolicySet {
error!("unexpected game event type after election vote followup: {:?}", p); error!("unexpected game event type after election vote followup: {:?}", p);
return Err(Role::Null); return Err(Role::Null);
@ -71,9 +75,12 @@ impl GameMaster {
} }
let param = param.unwrap(); let param = param.unwrap();
let policy = Policy::from_string(param.to_string()); let policy = Policy::from_string(param.to_string());
self.game_policy_veto(policy.clone())?; let ge = self.game_policy_veto(policy.clone())?;
self.game_ends_with(policy.clone())?; self.room.send(ge.serialize());
self.game_policy_enact(policy.clone())?; let ge = self.game_ends_with(policy.clone())?;
self.room.send(ge.serialize());
let ge = self.game_policy_enact(policy.clone())?;
self.room.send(ge.serialize());
} }
} }
@ -117,7 +124,7 @@ mod tests {
#[test] #[test]
fn new_mockroom() { fn new_mockroom() {
let _ = GameMaster::new(Box::new(MockRoom::create())); let _ = GameMaster::new(Box::new(MockRoom::create("__gm__".to_string())));
} }
#[test] #[test]
@ -127,23 +134,23 @@ mod tests {
mrs mrs
} }
let mut mrs = get(); let mut mrs = get();
let r = mrs.create(); let r = mrs.create("__gm__".to_string());
let _ = GameMaster::new(r); let _ = GameMaster::new(r);
} }
#[test] #[test]
fn new_mockrooms() { fn new_mockrooms() {
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r = mrs.create(); let r = mrs.create("__gm__".to_string());
let _ = GameMaster::new(r); let _ = GameMaster::new(r);
} }
#[test] #[test]
fn scrape_until_get() { fn scrape_until_get() {
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r = mrs.create(); let r = mrs.create("__gm__".to_string());
let room_id = r.room_id().clone(); let room_id = r.room_id().clone();
let r2 = mrs.join(room_id); let r2 = mrs.join("r2".to_string(), room_id);
assert!(r2.is_ok()); assert!(r2.is_ok());
let mut r2 = r2.unwrap(); let mut r2 = r2.unwrap();
let mut gm = GameMaster::new(r); let mut gm = GameMaster::new(r);

View File

@ -55,15 +55,15 @@ mod tests {
fn run_lobby() { fn run_lobby() {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r1 = mrs.create(); let r1 = mrs.create("__gm__".to_string());
let room_id = r1.room_id(); let room_id = r1.room_id();
let mut gm = GameMaster::new(r1); let mut gm = GameMaster::new(r1);
for i in 0..config::MIN_PLAYERS-1 { for i in 0..config::MIN_PLAYERS-1 {
let mut r2 = mrs.join(room_id.clone()).unwrap(); let mut r2 = mrs.join(i.to_string(), room_id.clone()).unwrap();
r2.send(format!(r#"{{ r2.send(format!(r#"{{
"msgtype": "m.text", "msgtype": "m.text",
"body": "{{\"GameEventType\": \"GameStart\"}}" "body": "{{\"GameEventType\": \"GameStart\"}}"
}}"#)).unwrap(); }}"#)).unwrap();
let ready = gm.run_lobby(); let ready = gm.run_lobby();
assert!(ready.is_err() == (i != 3), "want {:?} for ready.is_err #{:?}, which is {:?}", i != 3, i, ready.is_err()); assert!(ready.is_err() == (i != 3), "want {:?} for ready.is_err #{:?}, which is {:?}", i != 3, i, ready.is_err());
} }
@ -72,12 +72,12 @@ mod tests {
for k in gm.lobby.players.keys() { for k in gm.lobby.players.keys() {
players1.push(k.clone()); players1.push(k.clone());
} }
let mut r2 = mrs.join(room_id.clone()).unwrap(); let mut r2 = mrs.join("r2".to_string(), room_id.clone()).unwrap();
r2.send(format!(r#"{{ r2.send(format!(r#"{{
"msgtype": "m.text", "msgtype": "m.text",
"body": "{{\"GameEventType\": \"GameStart\"}}" "body": "{{\"GameEventType\": \"GameStart\"}}"
}}"#)).unwrap(); }}"#)).unwrap();
assert!(gm.run_lobby().is_ok()); assert!(gm.run_lobby().is_ok());
assert!(gm.lobby.players.len() == 5, "secnd run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync()); assert!(gm.lobby.players.len() == 5, "secnd run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync());
let players2 = gm.lobby.players.keys(); let players2 = gm.lobby.players.keys();
assert!(format!("{:?}", players1) == format!("{:?}", players2)); assert!(format!("{:?}", players1) == format!("{:?}", players2));

View File

@ -110,7 +110,7 @@ mod tests {
fn run_game_setup() { fn run_game_setup() {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r1 = mrs.create(); let r1 = mrs.create("__gm__".to_string());
let mut gm = GameMaster::new(r1); let mut gm = GameMaster::new(r1);
let r = gm.run_game_setup(); let r = gm.run_game_setup();
assert!(!r.is_ok()); assert!(!r.is_ok());
@ -128,7 +128,7 @@ mod tests {
fn setup_gather_candidates() { fn setup_gather_candidates() {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r1 = mrs.create(); let r1 = mrs.create("__gm__".to_string());
let mut gm = GameMaster::new(r1); let mut gm = GameMaster::new(r1);
assert!(gm.setup_gather_candidates().is_ok()); assert!(gm.setup_gather_candidates().is_ok());
for i in 0..config::MIN_PLAYERS { for i in 0..config::MIN_PLAYERS {
@ -147,7 +147,7 @@ mod tests {
fn setup_set_roles() { fn setup_set_roles() {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r1 = mrs.create(); let r1 = mrs.create("__gm__".to_string());
let mut gm = GameMaster::new(r1); let mut gm = GameMaster::new(r1);
assert!(gm.setup_set_roles().is_err()); assert!(gm.setup_set_roles().is_err());
for i in 0..config::MIN_PLAYERS { for i in 0..config::MIN_PLAYERS {
@ -168,7 +168,7 @@ mod tests {
fn setup_order_candidates() { fn setup_order_candidates() {
init(); init();
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
let r1 = mrs.create(); let r1 = mrs.create("__gm__".to_string());
let mut gm = GameMaster::new(r1); let mut gm = GameMaster::new(r1);
assert!(gm.setup_order_candidates().is_ok()); assert!(gm.setup_order_candidates().is_ok());
gm.candidate_presidents = ["1".to_string()].to_vec(); gm.candidate_presidents = ["1".to_string()].to_vec();

View File

@ -11,14 +11,15 @@ pub struct MockRoom {
room_id: String, room_id: String,
events_s: Sender<Vec<Event>>, events_s: Sender<Vec<Event>>,
events_r: Receiver<Vec<Event>>, events_r: Receiver<Vec<Event>>,
pub sender: String,
} }
impl MockRoom { impl MockRoom {
pub fn create() -> MockRoom { pub fn create(sender: String) -> MockRoom {
MockRoom::join(rands()) MockRoom::join(sender, rands())
} }
pub fn join(room_id: String) -> MockRoom { pub fn join(sender: String, room_id: String) -> MockRoom {
let (s, r) = unbounded(); let (s, r) = unbounded();
s.send(vec![]).ok().unwrap(); s.send(vec![]).ok().unwrap();
let mut mr = MockRoom { let mut mr = MockRoom {
@ -26,12 +27,12 @@ impl MockRoom {
room_id: room_id.clone(), room_id: room_id.clone(),
events_s: s, events_s: s,
events_r: r, events_r: r,
sender: sender,
}; };
let id = rands(); mr.send_as(mr.sender.clone(), format!(r#"{{
mr.send_as(id.clone(), format!(r#"{{
"displayname": "{}", "displayname": "{}",
"membership": "join" "membership": "join"
}}"#, id.clone())).unwrap(); }}"#, mr.sender.clone())).unwrap();
mr mr
} }
@ -111,13 +112,13 @@ mod tests {
use super::rands; use super::rands;
fn _dummy() -> MockRoom { fn _dummy() -> MockRoom {
let mut r = MockRoom::create(); let mut r = MockRoom::create(rands());
r.since = "1".to_string(); r.since = "1".to_string();
let mut events = r.events_r.recv().ok().unwrap(); let mut events = r.events_r.recv().ok().unwrap();
for i in 0..5 { for i in 0..5 {
events.push(Event{ events.push(Event{
sender: i.to_string(), sender: i.to_string(),
since: i.to_string(), since: i.to_string(),
body: i.to_string(), body: i.to_string(),
}); });
} }
@ -134,14 +135,14 @@ mod tests {
#[test] #[test]
fn create() { fn create() {
let mut r: MockRoom = MockRoom::create(); let mut r: MockRoom = MockRoom::create(rands());
println!("{:?}", r.sync()); println!("{:?}", r.sync());
} }
#[test] #[test]
fn join() { fn join() {
let rid = "a".to_string(); let rid = "a".to_string();
let mut r: MockRoom = MockRoom::join(rid.to_string()); let mut r: MockRoom = MockRoom::join(rands(), rid.to_string());
assert!(r.room_id == rid); assert!(r.room_id == rid);
let events = r.sync(); let events = r.sync();
let mut found = false; let mut found = false;

View File

@ -17,16 +17,18 @@ impl MockRooms {
} }
impl Rooms for MockRooms { impl Rooms for MockRooms {
fn create(&mut self) -> Box<dyn Room> { fn create(&mut self, sender: String) -> Box<dyn Room> {
let room = MockRoom::create(); let room = MockRoom::create(sender);
let _room = room.room(); let _room = room.room();
self.rooms.push(room); self.rooms.push(room);
Box::new(_room) Box::new(_room)
} }
fn join(&self, room_id: String) -> Result<Box<dyn Room>, &str> { fn join(&self, sender: String, room_id: String) -> Result<Box<dyn Room>, &str> {
for r in &self.rooms { for r in &self.rooms {
if r.room_id() == room_id { if r.room_id() == room_id {
let mut r = r.clone();
r.sender = sender;
let mut r = r.room(); let mut r = r.room();
r.send(format!(r#"{{ r.send(format!(r#"{{
"displayname": "{}", "displayname": "{}",
@ -48,9 +50,9 @@ mod tests {
fn _dummy() -> MockRooms { fn _dummy() -> MockRooms {
let mut mrs = MockRooms::new(); let mut mrs = MockRooms::new();
for i in 0..5 { for i in 0..5 {
let random = MockRoom::create(); let random = MockRoom::create(i.to_string());
mrs.rooms.push(random); mrs.rooms.push(random);
let joined = MockRoom::join(i.to_string()); let joined = MockRoom::join(i.to_string(), i.to_string());
mrs.rooms.push(joined); mrs.rooms.push(joined);
} }
assert!(mrs.rooms.len() == 10); assert!(mrs.rooms.len() == 10);
@ -67,7 +69,7 @@ mod tests {
fn create() { fn create() {
let mut mrs = _dummy(); let mut mrs = _dummy();
let was = mrs.rooms.len(); let was = mrs.rooms.len();
let _ = mrs.create(); let _ = mrs.create("abc".to_string());
let is = mrs.rooms.len(); let is = mrs.rooms.len();
assert!(was+1 == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); assert!(was+1 == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is);
} }
@ -76,7 +78,7 @@ mod tests {
fn join_404() { fn join_404() {
let mrs = _dummy(); let mrs = _dummy();
let was = mrs.rooms.len(); let was = mrs.rooms.len();
let r = mrs.join("does not exist".to_string()); let r = mrs.join("?".to_string(), "does not exist".to_string());
let is = mrs.rooms.len(); let is = mrs.rooms.len();
assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is);
assert!(!r.is_ok()); assert!(!r.is_ok());
@ -86,7 +88,7 @@ mod tests {
fn join_found() { fn join_found() {
let mrs = _dummy(); let mrs = _dummy();
let was = mrs.rooms.len(); let was = mrs.rooms.len();
let r = mrs.join("0".to_string()); let r = mrs.join("?".to_string(), "0".to_string());
let is = mrs.rooms.len(); let is = mrs.rooms.len();
assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is);
assert!(r.is_ok()); assert!(r.is_ok());
@ -96,8 +98,8 @@ mod tests {
#[test] #[test]
fn join_clobber() { fn join_clobber() {
let mrs = _dummy(); let mrs = _dummy();
let mut a = mrs.join("0".to_string()).ok().unwrap(); let mut a = mrs.join("?".to_string(), "0".to_string()).ok().unwrap();
let mut b = mrs.join("0".to_string()).ok().unwrap(); let mut b = mrs.join("?".to_string(), "0".to_string()).ok().unwrap();
assert!(a.room_id() == b.room_id()); assert!(a.room_id() == b.room_id());

View File

@ -18,7 +18,7 @@ mod tests {
#[test] #[test]
fn mockroom() { fn mockroom() {
fn gen() -> impl Room { fn gen() -> impl Room {
let r = MockRoom::create(); let r = MockRoom::create("123".to_string());
r r
} }
gen(); gen();

View File

@ -1,8 +1,8 @@
use super::room::Room; use super::room::Room;
pub trait Rooms { pub trait Rooms {
fn create(&mut self) -> Box<dyn Room>; fn create(&mut self, sender: String) -> Box<dyn Room>;
fn join(&self, room_id: String) -> Result<Box<dyn Room>, &str>; fn join(&self, sender: String, room_id: String) -> Result<Box<dyn Room>, &str>;
} }
#[cfg(test)] #[cfg(test)]
@ -18,7 +18,7 @@ mod tests {
r r
} }
let mut rooms = gen(); let mut rooms = gen();
let mut room_ptr: Box<dyn Room> = rooms.create(); let mut room_ptr: Box<dyn Room> = rooms.create("abc".to_string());
assert!(room_ptr.send("hi".to_string()).is_ok()); assert!(room_ptr.send("hi".to_string()).is_ok());
} }
} }