chiark / gitweb /
leave game
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 23 Nov 2020 00:07:04 +0000 (00:07 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 23 Nov 2020 00:07:04 +0000 (00:07 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/bin/otter.rs
src/cmdlistener.rs
src/commands.rs
src/config.rs

index b885a46e786ae2771ae520ea5f6e4d5a656ec004..d8b41ddd3b04943f09d9cabfd0e177fadf99e4bb 100644 (file)
@@ -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<String,PlayerId>
@@ -849,18 +866,8 @@ mod join_game {
     let args = parse_args::<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<String>) ->Result<(),AE> {
+    let args = parse_args::<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)]
index 47233f20971c61240c73b5ba035ebffc4ca3e86d..ab23556feb97fe42389438a470c5573d5e2cec26 100644 (file)
@@ -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("<partial data!>".into())
+                ,
+                (||{
+                  let (record, _) = ag.lookup(ipl?.acctid).ok()?;
+                  Some(record.account.to_string())
+                })()
+                .unwrap_or("<account deleted>".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")?;
     }
index 68772a216d1ef4be10086ab5f9327c1cf5117107..047780242fc2415d86db7294da976ae455bf869f 100644 (file)
@@ -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<TablePermission> },
index 1ef852b994815899aefb489b4fb69ea9e9a2aa3b..2883433ab33e3b5d3515748bf440ac4d7582cd3c 100644 (file)
@@ -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;