From: Ian Jackson Date: Sat, 28 Nov 2020 11:49:26 +0000 (+0000) Subject: send updates for player add/remove X-Git-Tag: otter-0.2.0~305 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=35c7703438a25b29c81736920bc26c3f25d299c0;p=otter.git send updates for player add/remove Signed-off-by: Ian Jackson --- diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index a207373e..78c4731f 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -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) { + who: &Html, + log: &mut Vec) + -> Vec + { 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) }, }; diff --git a/src/global.rs b/src/global.rs index 727ca872..9cacd9d4 100644 --- a/src/global.rs +++ b/src/global.rs @@ -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) + pub fn players_remove(&mut self, old_players_set: &HashSet) -> Result, Option) + (Option, Option, 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) } diff --git a/src/imports.rs b/src/imports.rs index b5abc42b..9dfd0a8d 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -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}; diff --git a/src/session.rs b/src/session.rs index f3abea29..8466e849 100644 --- a/src/session.rs +++ b/src/session.rs @@ -47,10 +47,6 @@ struct DataLoad { last_log_ts: String, players : HashMap, } -#[derive(Serialize,Debug)] -struct DataLoadPlayer { - dasharray : String, -} #[derive(Deserialize)] struct SessionForm { @@ -81,23 +77,8 @@ fn session(form : Json, layout: Option) 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)?; diff --git a/src/updates.rs b/src/updates.rs index 09b108c1..5aff07f1 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -47,6 +47,8 @@ pub enum PreparedUpdateEntry { op : PieceUpdateOp, }, SetTableSize(Pos), + AddPlayer { player: PlayerId, data: DataLoadPlayer }, + RemovePlayer { player: PlayerId }, Log (Arc), Error (Option /* none: all */, ErrorSignaledViaUpdate), } @@ -62,6 +64,11 @@ pub struct PreparedPieceState { pub uos: Vec, } +#[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, } @@ -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 PieceUpdateOp { @@ -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)