chiark / gitweb /
send updates for player add/remove
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 28 Nov 2020 11:49:26 +0000 (11:49 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 28 Nov 2020 11:49:26 +0000 (11:49 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/cmdlistener.rs
src/global.rs
src/imports.rs
src/session.rs
src/updates.rs

index a207373e739900b237642e6ab494086921a44193..78c4731f3de20473c08a31960ca54ab948d3e108 100644 (file)
@@ -336,13 +336,13 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
         tz,
         tokens_revealed: default(),
       };
-      let (player, logentry) = ig.player_new(gpl, ipl, logentry)?;
+      let (player, update, logentry) = ig.player_new(gpl, ipl, logentry)?;
 
       let atr = ig.player_access_reset(ag, player, auth.therefore_ok())?;
 
       (U{ pcs: vec![],
           log: vec![ logentry ],
-          raw: None },
+          raw: Some(vec![ update ] )},
        Resp::JoinGame { nick, player, token: atr },
        ig)
     },
@@ -447,7 +447,7 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
 
       let got = ig.players_remove(&[player].iter().cloned().collect())?;
 
-      let (gpl, ipl) = got.into_iter().next()
+      let (gpl, ipl, update) = got.into_iter().next()
         .ok_or(PlayerNotFound)?;
 
       let html = Html(
@@ -468,7 +468,7 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
 
       (U{ pcs: vec![],
           log: vec![ LogEntry { html }],
-          raw: None },
+          raw: Some(vec![ update ]) },
        Fine, ig)
     },
 
@@ -580,7 +580,10 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
 
       #[throws(InternalError)]
       fn remove_old_players(ag: &AccountsGuard, ig: &mut InstanceGuard,
-                            who: &Html, log: &mut Vec<LogEntry>) {
+                            who: &Html,
+                            log: &mut Vec<LogEntry>)
+                            -> Vec<PreparedUpdateEntry>
+      {
         let owner_account = ig.name.account.to_string();
         let eacl = EffectiveACL {
           owner_account: Some(&owner_account),
@@ -604,7 +607,8 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
           };
         };
 
-        for (gpl, _ipl) in ig.players_remove(&remove)? {
+        let mut updates = Vec::new();
+        for (gpl, _ipl, update) in ig.players_remove(&remove)? {
           let show = if let Some(gpl) = gpl {
             htmlescape::encode_minimal(&gpl.nick)
           } else {
@@ -613,14 +617,17 @@ fn execute_game_insn<'cs, 'igr, 'ig : 'igr>(
           log.push(LogEntry {
             html: Html(format!("{} removed a player {}", &who.0, &show)),
           });
+          updates.push(update);
         }
+
+        updates
       }
 
-      remove_old_players(&ag, ig, who, &mut log)?;
+      let updates = remove_old_players(&ag, ig, who, &mut log)?;
 
       (U{ pcs: vec![ ],
           log,
-          raw: None },
+          raw: Some(updates) },
        Fine, ig)
     },
   };
index 727ca8724818299ed360673efb4d0bf0f6f5a07c..9cacd9d418665e0e4b819adf3b598b2ff20dac47 100644 (file)
@@ -10,6 +10,7 @@ use std::sync::PoisonError;
 use slotmap::dense as sm;
 
 type ME = MgmtError;
+type ESU = ErrorSignaledViaUpdate;
 
 // ---------- newtypes and type aliases ----------
 
@@ -460,7 +461,7 @@ impl<'ig> InstanceGuard<'ig> {
   #[throws(MgmtError)]
   pub fn player_new(&mut self, gnew: GPlayerState, inew: IPlayerState,
                     logentry: LogEntry)
-                    -> (PlayerId, /* todo some game update,*/ LogEntry) {
+                    -> (PlayerId, PreparedUpdateEntry, LogEntry) {
     // saving is fallible, but we can't attempt to save unless
     // we have a thing to serialise with the player in it
     self.check_new_nick(&gnew.nick)?;
@@ -470,6 +471,10 @@ impl<'ig> InstanceGuard<'ig> {
     let player = self.c.g.gs.players.insert(gnew);
     let u = PlayerUpdates::new_begin(&self.c.g.gs).new();
     let record = PlayerRecord { u, ipl: inew };
+    let update = PreparedUpdateEntry::AddPlayer {
+      player,
+      data: DataLoadPlayer::from_player(self, player),
+    };
     self.c.g.iplayers.insert(player, record);
 
     (||{
@@ -486,7 +491,7 @@ impl<'ig> InstanceGuard<'ig> {
     (||{
       
     })(); // <- No ?, ensures that IEFE is infallible (barring panics)
-    (player, logentry)
+    (player, update, logentry)
   }
 
   #[throws(MgmtError)]
@@ -524,10 +529,10 @@ impl<'ig> InstanceGuard<'ig> {
 
   //  #[throws(InternalError)]
   //  https://github.com/withoutboats/fehler/issues/62
-  pub fn players_remove(&mut self, oldplayers: &HashSet<PlayerId>)
+  pub fn players_remove(&mut self, old_players_set: &HashSet<PlayerId>)
                         ->
     Result<Vec<
-        (Option<GPlayerState>, Option<IPlayerState>)
+        (Option<GPlayerState>, Option<IPlayerState>, PreparedUpdateEntry)
         >, InternalError>
   {
     // We have to filter this player out of everything
@@ -536,7 +541,8 @@ impl<'ig> InstanceGuard<'ig> {
     // We make a copy so if the save fails, we can put everything back
 
     let mut players = self.c.g.gs.players.clone();
-    let old_gpls : Vec<_> = oldplayers.iter().cloned().map(|oldplayer| {
+    let old_players : Vec<_> = old_players_set.iter().cloned().collect();
+    let old_gpls : Vec<_> = old_players.iter().cloned().map(|oldplayer| {
       players.remove(oldplayer)
     }).collect();
 
@@ -555,7 +561,7 @@ impl<'ig> InstanceGuard<'ig> {
 
     let held_by_old = |p: &PieceState| if_chain! {
       if let Some(held) = p.held;
-      if oldplayers.contains(&held);
+      if old_players_set.contains(&held);
       then { true }
       else { false }
     };
@@ -615,9 +621,11 @@ impl<'ig> InstanceGuard<'ig> {
       }
       buf.finish();
 
-      self.remove_clients(oldplayers, ErrorSignaledViaUpdate::PlayerRemoved);
-      self.tokens_deregister_for_id(|id:PlayerId| oldplayers.contains(&id));
-      let old_ipls : Vec<_> = oldplayers.iter().cloned().map(
+      self.remove_clients(old_players_set, ESU::PlayerRemoved);
+      self.tokens_deregister_for_id(
+        |id:PlayerId| old_players_set.contains(&id)
+      );
+      let old_ipls : Vec<_> = old_players.iter().cloned().map(
         |oldplayer| self.iplayers.remove(oldplayer)
           .map(|ipr| ipr.ipl)
       ).collect();
@@ -629,10 +637,16 @@ impl<'ig> InstanceGuard<'ig> {
       old_ipls
     })(); // <- No ?, ensures that IEFE is infallible (barring panics)
 
-    let old = itertools::zip(
+    let updates = old_players.iter().cloned().map(
+      |player| PreparedUpdateEntry::RemovePlayer { player }
+    );
+
+    let old = izip!(
       old_gpls,
       old_ipls,
+      updates
     ).collect();
+
     Ok(old)
   }
 
@@ -653,7 +667,7 @@ impl<'ig> InstanceGuard<'ig> {
     // ppoint of no return
     (||{
       self.remove_clients(&[player].iter().cloned().collect(),
-                          ErrorSignaledViaUpdate::TokenRevoked);
+                          ESU::TokenRevoked);
     })(); // <- No ?, ensures that IEFE is infallible (barring panics)
   }
 
index b5abc42b4515c89edffdab061f9ce3374e4f8a4a..9dfd0a8d8af5034653c401689e942069ff47f657 100644 (file)
@@ -48,8 +48,7 @@ pub use flexi_logger::{self, LogSpecification};
 pub use fs2::FileExt;
 pub use if_chain::if_chain;
 pub use index_vec::{define_index_type, index_vec, IndexVec, IndexSlice};
-pub use itertools::EitherOrBoth;
-pub use itertools::Itertools;
+pub use itertools::{izip, EitherOrBoth, Itertools};
 pub use lazy_static::lazy_static;
 pub use log::{log, log_enabled};
 pub use log::{trace, debug, info, warn, error};
index f3abea29bba40edd6471ecb92aae02ac7e2c1822..8466e84906533b24065e41d7c0cd8636ad765f72 100644 (file)
@@ -47,10 +47,6 @@ struct DataLoad {
   last_log_ts: String,
   players : HashMap<PlayerId, DataLoadPlayer>,
 }
-#[derive(Serialize,Debug)]
-struct DataLoadPlayer {
-  dasharray : String,
-}
 
 #[derive(Deserialize)]
 struct SessionForm {
@@ -81,23 +77,8 @@ fn session(form : Json<SessionForm>, layout: Option<PresentationLayout>)
 
     let mut load_players = HashMap::new();
     for (player, _pl) in &ig.gs.players {
-      let kd : slotmap::KeyData = player.into();
-      let n = kd.get_idx_version().0;
-      let n = if n != 0 { n.try_into().unwrap() }
-              else { ig.gs.players.capacity() };
-      assert!(n != 0);
-      let mut dasharray = String::with_capacity(n*3 + 4);
-      for dash in iter::once("3").chain(
-        iter::repeat("1").take(n-1))
-      {
-        write!(&mut dasharray, "{} 1 ", &dash).unwrap();
-      }
-      let spc = dasharray.pop();
-      assert_eq!(spc,Some(' '));
-
-      load_players.insert(player, DataLoadPlayer {
-        dasharray,
-      });
+      let dataload = DataLoadPlayer::from_player(ig, player);
+      load_players.insert(player, dataload);
     }
 
     let gpl = ig.gs.players.byid_mut(player)?;
index 09b108c1d45904ce4dfa308c47b10f5bf559bc13..5aff07f18beece904455c072cb436b688aedcb51 100644 (file)
@@ -47,6 +47,8 @@ pub enum PreparedUpdateEntry {
     op : PieceUpdateOp<PreparedPieceState,ZLevel>,
   },
   SetTableSize(Pos),
+  AddPlayer { player: PlayerId, data: DataLoadPlayer },
+  RemovePlayer { player: PlayerId },
   Log (Arc<CommittedLogEntry>),
   Error (Option<ClientId> /* none: all */, ErrorSignaledViaUpdate),
 }
@@ -62,6 +64,11 @@ pub struct PreparedPieceState {
   pub uos: Vec<UoDescription>,
 }
 
+#[derive(Serialize,Debug)]
+pub struct DataLoadPlayer {
+  dasharray : String,
+}
+
 // ---------- piece updates ----------
 
 #[derive(Debug,Serialize)]
@@ -104,6 +111,8 @@ enum TransmitUpdateEntry<'u> {
     ns: &'u PreparedPieceState,
   },
   SetTableSize(Pos),
+  AddPlayer { player: PlayerId, data: &'u DataLoadPlayer },
+  RemovePlayer { player: PlayerId },
   #[serde(serialize_with="serialize_logentry")]
   Log(TransmitUpdateLogEntry<'u>),
   Error(&'u ErrorSignaledViaUpdate),
@@ -121,7 +130,6 @@ struct FormattedLogEntry<'u> {
 
 // ---------- prepared updates, queued in memory ----------
 
-
 pub struct PlayerUpdatesBuildContext {
   pub(self) u1: Arc<PreparedUpdate>,
 }
@@ -187,7 +195,11 @@ impl PreparedUpdateEntry {
       Log(logent) => {
         logent.logent.html.0.as_bytes().len() * 28
       },
+      AddPlayer { player:_, data: DataLoadPlayer { dasharray } } => {
+        dasharray.as_bytes().len() + 100
+      },
       SetTableSize(_) |
+      RemovePlayer { player:_ } |
       Error(_,_) => {
         100
       },
@@ -195,6 +207,27 @@ impl PreparedUpdateEntry {
   }
 }
 
+impl DataLoadPlayer {
+  pub fn from_player(ig: &Instance, player: PlayerId) -> Self {
+    let kd : slotmap::KeyData = player.into();
+    let n = kd.get_idx_version().0;
+    let n = if n != 0 { n.try_into().unwrap() }
+    else { ig.gs.players.capacity() };
+    assert!(n != 0);
+    let mut dasharray = String::with_capacity(n*3 + 4);
+    for dash in iter::once("3").chain(
+      iter::repeat("1").take(n-1))
+    {
+      write!(&mut dasharray, "{} 1 ", &dash).unwrap();
+    }
+    let spc = dasharray.pop();
+    assert_eq!(spc,Some(' '));
+    DataLoadPlayer {
+      dasharray,
+    }
+  }
+}
+
 // ---------- PieceUpdatesOp ----------
 
 impl<NS,ZC> PieceUpdateOp<NS,ZC> {
@@ -499,6 +532,12 @@ impl PreparedUpdate {
         &PUE::SetTableSize(size) => {
           TUE::SetTableSize(size)
         },
+        &PUE::AddPlayer { player, ref data } => {
+          TUE::AddPlayer { player, data }
+        },
+        &PUE::RemovePlayer { player } => {
+          TUE::RemovePlayer { player }
+        },
         PUE::Error(c, e) => {
           if *c == None || *c == Some(dest) {
             TUE::Error(e)