From: Ian Jackson Date: Sat, 22 Aug 2020 15:50:40 +0000 (+0100) Subject: alter game create table X-Git-Tag: otter-0.2.0~1116 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=95502bdcfaae4d9e601a493862bf24ed38759f3f;p=otter.git alter game create table --- diff --git a/src/bin/otter.rs b/src/bin/otter.rs index b6ce9227..eac8c41e 100644 --- a/src/bin/otter.rs +++ b/src/bin/otter.rs @@ -179,15 +179,15 @@ fn main() { } struct Conn { - mc: MgmtChannel, + chan: MgmtChannel, } impl Conn { #[throws(AE)] fn cmd(&mut self, cmd: &MgmtCommand) -> MgmtResponse { use MgmtResponse::*; - self.mc.write(&cmd).context("send command")?; - let resp = self.mc.read().context("read response")?; + self.chan.write(&cmd).context("send command")?; + let resp = self.chan.read().context("read response")?; match &resp { Fine{..} | GamesList{..} => { }, AlterGame { error: None, .. } => { }, @@ -208,85 +208,93 @@ struct ConnForGame { pub conn: Conn, pub name: String, pub how: MgmtGameUpdateMode, -}; +} impl Deref for ConnForGame { type Target = Conn; - fn deref(cg: &ConnForGame) -> Conn { &cg.conn } + fn deref(&self) -> &Conn { &self.conn } } impl DerefMut for ConnForGame { - fn deref_mut(cg: &mut ConnForGame) -> Conn { &mut cg.conn } + fn deref_mut(&mut self) -> &mut Conn { &mut self.conn } } impl ConnForGame { #[throws(AE)] - fn alter_game(&mut self, insns: Vec, - f: &mut dyn FnMut(&MgmtGameInstruction, &MgmtGameResult) - -> Result) - -> Vec> { + fn alter_game(&mut self, insns: Vec, + f: &mut dyn FnMut(&MgmtGameResponse) -> Result<(),AE>) + -> Vec { + let insns_len = insns.len(); let cmd = MgmtCommand::AlterGame { name: self.name.clone(), how: self.how, - insns, + insns }; - let results = match self.cmd(&cmd) { - MgmtResponse::AlterGame { error: None, results } - if results.len() == cmd.insns.len() - => results, - wat => anyhow!("unexpected AlterGame response: {:?} => {:?}", - &cmd, &wat), + let responses = match self.cmd(&cmd)? { + MgmtResponse::AlterGame { error: None, responses } + if responses.len() == insns_len => { + responses + }, + wat => Err(anyhow!("unexpected AlterGame response: {:?} => {:?}", + &cmd, &wat))?, }; - for (insn, result) in insns.iter.zip(results.iter()) { - f(insn,result)?; + for response in &responses { + f(response)?; } - results + responses } - #[throws(AE)] - fn game_state(&mut self) -> (GameState, HashMap) { - let gs = self.alter_game( - vec![ MgmtGameInstruction::GetState { } ], - |..|Ok(()) + fn get_players(&mut self) -> + Result<(PlayerMap, HashMap),AE> + { + let mut players = self.alter_game( + vec![ MgmtGameInstruction::GetPlayers { } ], + &mut |_|Ok(()) )?; - let gs = match gs { - &[ GameState { gs } ] => gs, - wat => anyhow!("GetGstate got {:?}", &wat); + let players = match players.pop() { + Some(MgmtGameResponse::Players(players)) => players, + wat => Err(anyhow!("GetGstate got {:?}", &wat))?, }; let mut nick2id = HashMap::new(); - for (player, pstate) in &gs.players { + for (player, pstate) in players.iter() { + use hash_map::Entry::*; match nick2id.entry(pstate.nick.clone()) { - Occupied(oe) => anyhow!("game has duplicate nick {:?}, {} {}", - &nick, *oe.get(), player), - Vacant(ve) ve.insert(player), - } + Occupied(oe) => Err(anyhow!("game has duplicate nick {:?}, {} {}", + &pstate.nick, *oe.get(), player))?, + Vacant(ve) => ve.insert(player), + }; } - (gs, nick2id) + Ok((players, nick2id)) } } #[throws(E)] -fn connect(ma: &MainOpts) -> MgmtChannel { +fn connect(ma: &MainOpts) -> Conn { let unix = UnixStream::connect(SOCKET_PATH).context("connect to server")?; - let mut chan = MgmtChannel::new(unix)?; + let chan = MgmtChannel::new(unix)?; + let mut chan = Conn { chan }; chan.cmd(&MgmtCommand::SetScope { scope: ma.scope.clone().unwrap() })?; chan } -#[throws(E)] -fn setup_table(game: &mut ConnForGame, spec: &TableSpec) { +fn setup_table(chan: &mut ConnForGame, spec: &TableSpec) -> Result<(),AE> { + // xxx should delete old players + // create missing players let (added_players,) = { - let (gs, nick2id) = chan.game_state()?; + let (_, nick2id) = chan.get_players()?; - let mut inss = vec![]; - for psec in &spec.players { - if !nick2id.has_key(pspec.nick) { - insns.push(AddPlayer { nick: ps.nick }); + let mut insns = vec![]; + for pspec in &spec.players { + if !nick2id.contains_key(&pspec.nick) { + insns.push(MgmtGameInstruction::AddPlayer(PlayerState { + nick: pspec.nick.clone() + })); } } let mut added_players = HashSet::new(); - chan.alter_game(name, insns, |insn,result| { - let player = match result { - MgmtGameInstruction::AddPlayer { player } => player, - _ => anyhow!("AddPlayer strange answer: {:?} => {:?}",&insn,&result), + chan.alter_game(insns, &mut |response| { + let player = match response { + &MgmtGameResponse::AddPlayer(player) => player, + _ => Err(anyhow!("AddPlayer strange answer {:?}", + &response))?, }; added_players.insert(player); Ok(()) @@ -297,44 +305,52 @@ fn setup_table(game: &mut ConnForGame, spec: &TableSpec) { // ensure players have access tokens { - let (gs, nick2id) = chan.game_state()?; + let (_, nick2id) = chan.get_players()?; let mut insns = vec![]; let mut resetreport = vec![]; let mut resetspecs = vec![]; for pspec in &spec.players { - let player = nick2id.get(&pspec.nick) - .ok_or_else(anyhow!("player {:?} vanished or renamed!", - &pspec.nick))?; - match pspec.access.token_mgi(player) { - Some(insn) => insns.push(insn), - None if added_players.contains(player) => { - resetreport.push(player); - resetspecs.push(player); - }, - None => (), + let player = *nick2id.get(&pspec.nick) + .ok_or_else(||anyhow!("player {:?} vanished or renamed!", + &pspec.nick))?; + if let Some(access) = &pspec.access { + match access.token_mgi(player) { + Some(insn) => insns.push(insn), + None if added_players.contains(&player) => { + resetreport.push(player); + resetspecs.push((pspec, access)); + }, + None => (), + } }; } insns.push(MgmtGameInstruction::ResetPlayerAccesses { players: resetreport.clone(), }); - insns.push(MgmtGameInstruction::ReporPlayerAccesses { + insns.push(MgmtGameInstruction::ReportPlayerAccesses { players: resetreport.clone(), }); - let got_tokens = None; - chan.alter_game(insns, &mut |insn,result| { - if let PlayerAccessTokens { tokens } = result { - got_tokens = Some(tokens.clone()), + let mut got_tokens = None; + chan.alter_game(insns, &mut |response| { + if let MgmtGameResponse::PlayerAccessTokens { tokens } = response { + got_tokens = Some(tokens.clone()); } - }); + Ok(()) + })?; let got_tokens = match got_tokens { Some(t) if t.len() == resetreport.len() => t, - wat => anyhow!("Did not get expected ReportPlayerAccesses! {:?}", &wat)?, + wat => Err(anyhow!("Did not get expected ReportPlayerAccesses! {:?}", + &wat))?, }; - for (pspec, ptokens) in resetspecs.iter().zip(got_tokens.iter()) { - pspec.access.deliver_tokens(&pspec, &ptokens) - .context("deliver tokens for nick={:?}", &pspec.nick)?; + for ((pspec, access), ptokens) + in resetspecs.iter().zip(got_tokens.iter()) { + access.deliver_tokens(&pspec, &ptokens) + .with_context(||format!("deliver tokens for nick={:?}", + &pspec.nick))?; } } + + Ok(()) } mod create_table { @@ -373,19 +389,21 @@ mod create_table { >::Ok(spec) })().with_context(|| args.file.to_owned()).context("read game spec")?; + let mut chan = connect(&ma)?; + chan.cmd(&MgmtCommand::CreateGame { name: args.name.clone(), insns: vec![] })?; - let chan = ConnForGame { + let mut chan = ConnForGame { conn: chan, name: args.name.clone(), how: MgmtGameUpdateMode::Bulk, }; - setup_table(&chan &spec.table)?; + setup_table(&mut chan, &spec)?; - eprintln!("CREATE-TABLE DID SETUP_TABLE NEEDS GAMESPEC", &ma, &args); + eprintln!("CREATE-TABLE DID SETUP_TABLE NEEDS GAMESPEC"); // xxx } inventory::submit!{Subcommand( diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 50d740e6..f5d5abc6 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -203,7 +203,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { }, CreateGame { name, insns } => { - let gs = GameState { + let gs = crate::gamestate::GameState { pieces : Default::default(), players : Default::default(), log : Default::default(), @@ -339,18 +339,18 @@ fn execute_for_game(cs: &CommandStream, ig: &mut InstanceGuard, mut insns: Vec, how: MgmtGameUpdateMode) -> MgmtResponse { let mut uh = UpdateHandler::from_how(how); - let mut results = Vec::with_capacity(insns.len()); + let mut responses = Vec::with_capacity(insns.len()); let ok = (||{ for insn in insns.drain(0..) { - let (upieces, ulogs, resp) = execute_game_insn(ig, insn)?; + let (upieces, ulogs, resp) = execute_game_insn(cs, ig, insn)?; uh.accumulate(ig, upieces, ulogs)?; - results.push(resp); + responses.push(resp); } uh.complete(cs,ig)?; Ok(None) })(); MgmtResponse::AlterGame { - results, + responses, error: ok.unwrap_or_else(Some) } } @@ -361,12 +361,13 @@ const XXX_DEFAULT_POSD : Pos = [5,5]; const CREATE_PIECES_MAX : u32 = 300; #[throws(ME)] -fn execute_game_insn(ig: &mut InstanceGuard, update: MgmtGameInstruction) +fn execute_game_insn(cs: &CommandStream, + ig: &mut InstanceGuard, update: MgmtGameInstruction) -> (Vec<(PieceId,PieceUpdateOp<()>)>, Vec, - MgmtGameResult) { + MgmtGameResponse) { use MgmtGameInstruction::*; - use MgmtGameResult::*; + use MgmtGameResponse::*; match update { Noop { } => (vec![], vec![], Fine { }), @@ -378,7 +379,7 @@ fn execute_game_insn(ig: &mut InstanceGuard, update: MgmtGameInstruction) vec![ LogEntry { html: format!("The facilitator added a player xxx"), } ], - MgmtGameResult::AddPlayer { player }) + MgmtGameResponse::AddPlayer(player)) }, RemovePlayer(player) => { @@ -386,6 +387,10 @@ fn execute_game_insn(ig: &mut InstanceGuard, update: MgmtGameInstruction) (vec![], vec![], Fine{}) }, + GetPlayers { } => { + let players = ig.gs.players.clone(); + (vec![], vec![], Players(players)) + }, ResetPlayerAccesses { players } => { let tokens = ig.players_access_reset(&players)? @@ -398,6 +403,19 @@ fn execute_game_insn(ig: &mut InstanceGuard, update: MgmtGameInstruction) (vec![], vec![], PlayerAccessTokens { tokens }) } + SetFixedPlayerAccess { player, token } => { + let authorised : AuthorisedSatisfactory = + authorise_scope(cs, &ManagementScope::Server)?; + let authorised = match authorised.into_inner() { + ManagementScope::Server => Authorised::::authorise(), + _ => panic!(), + }; + ig.player_access_register_fixed( + player, token, authorised + )?; + (vec![], vec![], Fine{}) + } + AddPiece(PiecesSpec{ pos,posd,count,face,info }) => { let gs = &mut ig.gs; let count = count.unwrap_or(1); @@ -511,6 +529,8 @@ impl CommandListener { use authproofs::*; use authproofs::AuthorisationError; +pub use authproofs::Authorised; + mod authproofs { use crate::imports::*; diff --git a/src/commands.rs b/src/commands.rs index 27c92574..207be925 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -17,9 +17,8 @@ pub enum MgmtCommand { pub enum MgmtResponse { Fine { }, Error { error: MgmtError }, - AlterGame { error: Option, results: Vec }, + AlterGame { error: Option, responses: Vec }, GamesList { games: Vec> }, - GameState { gs: GameState }, } #[derive(Debug,Serialize,Deserialize)] @@ -29,25 +28,26 @@ pub enum MgmtGameInstruction { // todo: RemovePiece AddPlayer(PlayerState), RemovePlayer(PlayerId), - GetState { }, + GetPlayers, ResetPlayerAccesses { players: Vec }, ReportPlayerAccesses { players: Vec }, SetFixedPlayerAccess { player: PlayerId, token: RawToken }, } #[derive(Debug,Serialize,Deserialize)] +pub enum MgmtGameResponse { + Fine { }, + AddPlayer(PlayerId), + PlayerAccessTokens { tokens: Vec> }, + Players(PlayerMap), +} + +#[derive(Debug,Copy,Clone,Serialize,Deserialize)] pub enum MgmtGameUpdateMode { Online, Bulk, } -#[derive(Debug,Serialize,Deserialize)] -pub enum MgmtGameResult { - Fine { }, - AddPlayer { player: PlayerId }, - PlayerAccessTokens { tokens: Vec> }, -} - #[derive(Debug,Clone,Error,Serialize,Deserialize)] pub enum MgmtError { ParseFailed(String), diff --git a/src/gamestate.rs b/src/gamestate.rs index 8868962f..35aa6051 100644 --- a/src/gamestate.rs +++ b/src/gamestate.rs @@ -58,6 +58,8 @@ pub struct PieceState { pub gen_before_lastclient : Generation, } +pub type PlayerMap = DenseSlotMap; + #[derive(Debug,Clone,Serialize,Deserialize)] pub struct PlayerState { pub nick : String, diff --git a/src/global.rs b/src/global.rs index 6cd882da..6edd2df4 100644 --- a/src/global.rs +++ b/src/global.rs @@ -48,7 +48,6 @@ pub struct Client { pub lastseen : Instant, } -pub type PlayerMap = DenseSlotMap; /* xxx #[derive(Serialize,Deserialize)] #[repr(transparent)] @@ -419,15 +418,18 @@ impl InstanceGuard<'_> { Ok(()) } - #[throws(OE)] - pub fn player_access_register_xxx(&mut self, token: RawToken, player: PlayerId) { - // xxx server has to not allow even facilitators to define tokens - // xxx this fn should become part of player_access_add + #[throws(MgmtError)] + pub fn player_access_register_fixed(&mut self, + player: PlayerId, token: RawToken, + _safe: Authorised + ) { + self.tokens_deregister_for_id(|id:PlayerId| id==player); let iad = InstanceAccessDetails { gref : self.gref.clone(), ident : player }; self.token_register(token, iad); + self.save_access_now()?; } #[throws(MgmtError)] @@ -439,7 +441,6 @@ impl InstanceGuard<'_> { for &player in players { self.c.g.gs.players.get(player).ok_or(MgmtError::PlayerNotFound)?; } - self.tokens_deregister_for_id(|id:PlayerId| players.contains(&id)); self.save_access_now()?; let mut tokens = vec![]; for &player in players { @@ -840,7 +841,8 @@ pub fn xxx_global_setup() { let player = g.player_new(PlayerState { nick : nick.to_string(), })?; - g.player_access_register_xxx(RawToken(token.to_string()), player)?; + g.player_access_register_fixed(player, RawToken(token.to_string()), + Authorised::authorise())?; } g.save_access_now().unwrap(); } diff --git a/src/spec.rs b/src/spec.rs index 10a9aba3..aed4fb69 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -33,11 +33,11 @@ pub struct PiecesSpec { #[typetag::serde(tag="access")] pub trait PlayerAccessSpec : Debug { - fn token_mgi(&self, player: PlayerId) -> Option { + fn token_mgi(&self, _player: PlayerId) -> Option { None } - #[throws(AE)] - fn deliver_token(&self, ps: &PlayerSpec, tokens: &[RawToken]) { } + fn deliver_tokens(&self, ps: &PlayerSpec, tokens: &[RawToken]) + -> Result<(),AE>; } #[derive(Debug,Serialize,Deserialize)] @@ -45,22 +45,25 @@ struct FixedToken(RawToken); #[typetag::serde] impl PlayerAccessSpec for FixedToken { - fn token_mgi(&self, player: PlayerId) -> Option { - Some(MgmtGmmeInstruction::SetFixedPlayerAccess { + fn token_mgi(&self, player: PlayerId) -> Option { + Some(MgmtGameInstruction::SetFixedPlayerAccess { player, token: self.0.clone(), }) } + #[throws(AE)] + fn deliver_tokens(&self, _ps: &PlayerSpec, _tokens: &[RawToken]) { } } #[derive(Debug,Serialize,Deserialize)] struct TokenOnStdout; #[typetag::serde] -impl PlayerAccessSpec for FixedToken { +impl PlayerAccessSpec for TokenOnStdout { #[throws(AE)] - fn deliver_tokens(&self, ps: &PlayerSpec, token: &[RawToken]) { - -> Result<(),anyhow::Error> { - println!("access nick={:?} token={}", &ps.nick, token); + fn deliver_tokens(&self, ps: &PlayerSpec, tokens: &[RawToken]) { + for token in tokens { + println!("access nick={:?} token={}", &ps.nick, token.0); + } } }