From: Ian Jackson Date: Mon, 23 Nov 2020 00:07:04 +0000 (+0000) Subject: leave game X-Git-Tag: otter-0.2.0~392 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=b40331e754926c85749a9a9365501a594cee3b0f;p=otter.git leave game Signed-off-by: Ian Jackson --- diff --git a/src/bin/otter.rs b/src/bin/otter.rs index b885a46e..d8b41ddd 100644 --- a/src/bin/otter.rs +++ b/src/bin/otter.rs @@ -299,7 +299,7 @@ fn main() { MapStore(|s| Ok(Some( FixedToken { token: RawToken(s.to_string()) }.into() ))), - "use fixed game access token TOKEN (for administrators, with --super, only; only \`reset', not \`redelivery', of tokens is possible)" + "use fixed game access token TOKEN (for administrators, with --super, only; only `reset', not `redelivery', of tokens is possible)" ); access.add_option(&["--no-access-token"], StoreConst(Some(PlayerAccessUnset.into())), @@ -538,6 +538,23 @@ impl ConnForGame { } responses } + + #[throws(AE)] + fn we_are_player(&mut self, ma: &MainOpts) + -> Option<(PlayerId, MgmtPlayerInfo)> + { + let players = { + let resp = self.alter_game(vec![MGI::Info], None)?; + match resp.as_slice() { + [MGR::Info(MgmtGameResponseGameInfo { players, .. })] => players, + x => throw!(anyhow!("unexpected response to game Info: {:?}", &x)), + }.clone() + }; + + players.into_iter().filter( + |(_,mpi)| &mpi.account == &ma.account + ).next() + } /* fn get_info(&mut self) -> Result< (MgmtGameResponseGameInfo, HashMap @@ -849,18 +866,8 @@ mod join_game { let args = parse_args::(args, &subargs, &ok_id, None); let mut chan = access_game(&ma, &args.table_name)?; - let players = { - let resp = chan.alter_game(vec![MGI::Info], None)?; - match resp.as_slice() { - [MGR::Info(MgmtGameResponseGameInfo { players, .. })] => players, - x => throw!(anyhow!("unexpected response to game Info: {:?}", &x)), - }.clone() - }; - let mut insns = vec![]; - match players.iter().filter( - |(_,mpi)| &mpi.account == &ma.account - ).next() { + match chan.we_are_player(&ma)? { None => { let nick = ma.nick.clone() .unwrap_or_else(|| ma.account.default_nick()); @@ -872,7 +879,7 @@ mod join_game { player.0.get_idx_version().0, &mpi.nick); let MgmtPlayerInfo { nick, account:_ } = mpi; if let Some(new_nick) = &ma.nick { - if nick != new_nick { + if &nick != new_nick { println!("changing nick to {:?}", &new_nick); let details = MgmtPlayerDetails { nick: ma.nick.clone() }; insns.push(MGI::UpdatePlayer { player, details }); @@ -925,6 +932,48 @@ mod join_game { )} } +//---------- leave-game ---------- + +mod leave_game { + use super::*; + + #[derive(Default,Debug)] + struct Args { + table_name: String, + } + + fn subargs(sa: &mut Args) -> ArgumentParser { + use argparse::*; + let mut ap = ArgumentParser::new(); + ap.refer(&mut sa.table_name).required() + .add_argument("TABLE-NAME",Store,"table name"); + ap + } + + fn call(_sc: &Subcommand, ma: MainOpts, args: Vec) ->Result<(),AE> { + let args = parse_args::(args, &subargs, &ok_id, None); + let mut chan = access_game(&ma, &args.table_name)?; + + let player = match chan.we_are_player(&ma)? { + None => { + println!("this account is not a player in that game"); + exit(EXIT_NOTFOUND); + }, + Some((player, _)) => player, + }; + + chan.alter_game(vec![MGI::LeaveGame(player)], None)?; + + Ok(()) + } + + inventory::submit!{Subcommand( + "leave-game", + "Leave a game", + call, + )} +} + //---------- library-list ---------- #[derive(Debug)] diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 47233f20..ab23556f 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -20,7 +20,6 @@ type CSE = anyhow::Error; use MgmtCommand::*; use MgmtResponse::*; -use MgmtError::*; type ME = MgmtError; from_instance_lock_error!{MgmtError} @@ -431,6 +430,38 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>( PlayerAccessToken(token), ig) }, + LeaveGame(player) => { + let account = &cs.current_account()?.notional_account; + let (ig, _auth) = cs.check_acl_manip_player_access + (ag, ig, player, TP::ModifyOtherPlayer)?; + + let got = ig.players_remove(&[player].iter().cloned().collect())?; + + let (gpl, ipl) = got.into_iter().next() + .ok_or(PlayerNotFound)?; + + let html = Html( + format!("{} [{}] left the game [{}]" + , + (|| Some(gpl?.nick))() + .unwrap_or("".into()) + , + (||{ + let (record, _) = ag.lookup(ipl?.acctid).ok()?; + Some(record.account.to_string()) + })() + .unwrap_or("".into()) + , + &account + ) + ); + + (U{ pcs: vec![], + log: vec![ LogEntry { html }], + raw: None }, + Fine, ig) + }, + DeletePiece(piece) => { let (ig, modperm, _) = cs.check_acl_modify_pieces(ag, ig)?; let p = ig.ipieces.as_mut(modperm) @@ -453,7 +484,7 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>( let ig = &mut **ig_g; let gs = &mut ig.gs; let count = count.unwrap_or(1); - if count > CREATE_PIECES_MAX { throw!(LimitExceeded) } + if count > CREATE_PIECES_MAX { throw!(ME::LimitExceeded) } let posd = posd.unwrap_or(DEFAULT_POS_DELTA); let mut updates = Vec::with_capacity(count as usize); @@ -732,7 +763,7 @@ impl CommandStream<'_> { }, Err(EOF) => break, Err(IO(e)) => Err(e).context("read command stream")?, - Err(Parse(s)) => MgmtResponse::Error { error : ParseFailed(s) }, + Err(Parse(s)) => MgmtResponse::Error { error : ME::ParseFailed(s) }, }; self.chan.write(&resp).context("swrite command stream")?; } diff --git a/src/commands.rs b/src/commands.rs index 68772a21..04778024 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -80,7 +80,7 @@ pub enum MgmtGameInstruction { JoinGame { details: MgmtPlayerDetails }, UpdatePlayer { player: PlayerId, details: MgmtPlayerDetails }, -// LeaveGame(PlayerId), xxx and in otter cli too + LeaveGame(PlayerId), ClearLog, SetACL { acl: Acl }, diff --git a/src/config.rs b/src/config.rs index 1ef852b9..2883433a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,6 +5,7 @@ use crate::imports::*; pub const EXIT_SPACE : i32 = 2; +pub const EXIT_NOTFOUND : i32 = 4; pub const EXIT_SITUATION : i32 = 8; pub const EXIT_USAGE : i32 = 12; pub const EXIT_DISASTER : i32 = 16;