chiark / gitweb /
wip new account etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 18 Oct 2020 13:14:17 +0000 (14:14 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 18 Oct 2020 13:14:17 +0000 (14:14 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/accounts.rs
src/api.rs
src/commands.rs
src/error.rs
src/gamestate.rs
src/global.rs
src/imports.rs
src/session.rs
src/spec.rs
src/sse.rs
src/updates.rs

index 8f71b8352a274e20b564540714fdb792454d79b9..e588b82cae44a37e53c228744c5e745cecc9050c 100644 (file)
@@ -86,24 +86,47 @@ pub struct AccountRecord {
 
 #[derive(Copy,Clone,Debug,Ord,PartialOrd,Eq,PartialEq)]
 pub struct TokenRevelation {
-  latest: Timestamp,
-  earliest: Timestamp,
+  pub latest: Timestamp,
+  pub earliest: Timestamp,
 }
 
 static ACCOUNTS : RwLock<Option<HashMap<AccountName, AccountRecord>>>
   = const_rwlock(None);
 
+// xxx load, incl reveleation expiry
+// xxx periodic token reveleation expiry
+
+pub fn save_accounts_now() -> Result<(), InternalError> {
+  panic!("xxx")
+}
+
 impl AccountRecord {
-  fn lookup(account: AccountName)
+  pub fn lookup(account: &AccountName)
             -> Option<MappedRwLockReadGuard<'static, AccountRecord>> {
     ACCOUNTS.read().map(
       |accounts| accounts?.get(account)
     )
   }
-  fn lookup_mut(account: AccountName)
+  pub fn lookup_mut_caller_must_save(account: &AccountName)
             -> Option<MappedRwLockWriteGuard<'static, AccountRecord>> {
     ACCOUNTS.write().map(
       |accounts| accounts?.get(account)
     )
   }
+  pub fn with_entry_mut<T, F>(account: &AccountName, f: F)
+                              -> Result<T, (InternalError, T)>
+  where F: FnOnce(Option<&mut AccountRecord>) -> T
+  {
+    let entry = AccountRecord::lookup_mut_caller_must_save(account);
+    let output = f(*entry);
+    let ok = if entry.is_some() { save_accounts_now() } else { Ok(()) };
+    match ok {
+      Ok(()) => Ok(output),
+      Err(e) => Err((e, output))
+    }
+  }
+
+  pub fn expire_tokens_revealed(&mut self) {
+    panic!("xxx")
+  }
 }
index 8d8a53604c37eb1949c16143bf01d026b6c4d26b..72edb05f8664e2c2cd2ec8bd78f6140861ee28db 100644 (file)
@@ -15,12 +15,19 @@ struct ApiPiece<O : ApiPieceOp> {
   op : O,
 }
 
+struct ApiPieceOpArgs<'a> {
+  gs: &'a mut GameState,
+  player: PlayerId,
+  pst: &'a PlayerState,
+  piece: PieceId,
+  p: &'a dyn Piece,
+  iplayers: &'a SecondarySlotMap<PlayerId, PlayerRecord>,
+  lens: &'a dyn Lens /* used for LogEntry and PieceId but not Pos */
+}
+
 trait ApiPieceOp : Debug {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece,
-        lens: &dyn Lens /* used for LogEntry and PieceId but not Pos */)
-        -> PieceUpdateFromOp;
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp;
 
   #[throws(OnlineError)]
   fn check_held(&self, pc: &PieceState, player: PlayerId) {
@@ -111,6 +118,8 @@ fn api_piece_op<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
   let player = cl.player;
   let gs = &mut g.gs;
   let ipieces = &g.ipieces;
+  let iplayers = &g.iplayers;
+  let pst = &iplayers.byid(player)?.pst;
   let _ = gs.players.byid(player)?;
   let lens = TransparentLens { };
   let piece = lens.decode_visible_pieceid(form.piece, player);
@@ -127,7 +136,12 @@ fn api_piece_op<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
 
     if u_gen > q_gen { throw!(PieceOpError::Conflict) }
     form.op.check_held(pc,player)?;
-    let (wrc, update, logents) = form.op.op(gs,player,piece,p.as_ref(),&lens)?;
+    let (wrc, update, logents) =
+      form.op.op(ApiPieceOpArgs {
+        gs, player, pst, piece, iplayers,
+        p: p.as_ref(),
+        lens: &lens,
+      })?;
     Ok::<_,ApiPieceOpError>((wrc, update, logents))
   })() {
     Err(ReportViaUpdate(poe)) => {
@@ -171,8 +185,8 @@ fn api_grab(form : Json<ApiPiece<ApiPieceGrab>>)
 }
 impl ApiPieceOp for ApiPieceGrab {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece, lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     let pl = gs.players.byid(player)?;
     let pc = gs.pieces.byid_mut(piece)?;
 
@@ -183,7 +197,7 @@ impl ApiPieceOp for ApiPieceGrab {
 
     let logent = LogEntry {
       html : Html(format!("{} grasped {}",
-                     &htmlescape::encode_minimal(&pl.nick),
+                     &htmlescape::encode_minimal(&pst.nick),
                      p.describe_pri(&lens.log_pri(piece, pc)).0)),
     };
 
@@ -207,23 +221,23 @@ impl ApiPieceOp for ApiPieceWrest {
   fn check_held(&self, _pc: &PieceState, _player: PlayerId) { }
 
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece, lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens,iplayers, .. } = a;
     let pl = gs.players.byid(player)?;
     let pc = gs.pieces.byid_mut(piece)?;
     let pcs = p.describe_pri(&lens.log_pri(piece, pc)).0;
 
     let was = pc.held;
     pc.held = Some(player);
-    let was = was.and_then(|p| gs.players.get(p));    
+    let was = was.and_then(|p| iplayers.get(p));    
 
     let update = PieceUpdateOp::Modify(());
 
-    let pls = &htmlescape::encode_minimal(&pl.nick);
+    let pls = &htmlescape::encode_minimal(&pst.nick);
 
     let logent = LogEntry { html : Html(match was {
         Some(was) => format!("{} wrested {} from {}", pls, pcs,
-                             &htmlescape::encode_minimal(&was.nick)),
+                             &htmlescape::encode_minimal(&was.pst.nick)),
         None => format!("{} wrested {}", pls, pcs),
     })};
 
@@ -243,8 +257,8 @@ fn api_ungrab(form : Json<ApiPiece<ApiPieceUngrab>>)
 }
 impl ApiPieceOp for ApiPieceUngrab {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece, lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     let pl = gs.players.byid(player).unwrap();
     let pc = gs.pieces.byid_mut(piece).unwrap();
 
@@ -255,7 +269,7 @@ impl ApiPieceOp for ApiPieceUngrab {
 
     let logent = LogEntry {
       html : Html(format!("{} released {}",
-                     &htmlescape::encode_minimal(&pl.nick),
+                     &htmlescape::encode_minimal(&pst.nick),
                      p.describe_pri(&lens.log_pri(piece, pc)).0)),
     };
 
@@ -276,8 +290,8 @@ fn api_raise(form : Json<ApiPiece<ApiPieceRaise>>)
 }
 impl ApiPieceOp for ApiPieceRaise {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId,
-        _p: &dyn Piece, _: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     let pc = gs.pieces.byid_mut(piece).unwrap();
     pc.zlevel = ZLevel { z : self.z.clone(), zg : gs.gen };
     let update = PieceUpdateOp::SetZLevel(());
@@ -295,8 +309,8 @@ fn api_move(form : Json<ApiPiece<ApiPieceMove>>) -> impl response::Responder<'st
 }
 impl ApiPieceOp for ApiPieceMove {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId,
-        _p: &dyn Piece, _lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     let pc = gs.pieces.byid_mut(piece).unwrap();
     let (pos, clamped) = self.0.clamped(gs.table_size);
     let logents = vec![];
@@ -322,15 +336,15 @@ fn api_pin(form : Json<ApiPiece<ApiPiecePin>>) -> impl response::Responder<'stat
 }
 impl ApiPieceOp for ApiPiecePin {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece, lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     let pc = gs.pieces.byid_mut(piece).unwrap();
     let pl = gs.players.byid(player).unwrap();
     pc.pinned = self.0;
     let update = PieceUpdateOp::Modify(());
     let logents = vec![ LogEntry { html: Html(format!(
       "{} {} {}",
-      &htmlescape::encode_minimal(&pl.nick),
+      &htmlescape::encode_minimal(&pst.nick),
       if pc.pinned { "pinned" } else { "unpinned" },
       p.describe_pri(&lens.log_pri(piece, pc)).0
     ))}];
@@ -350,8 +364,8 @@ fn api_uo(form : Json<ApiPiece<ApiPieceUo>>) -> impl response::Responder<'static
 }
 impl ApiPieceOp for ApiPieceUo {
   #[throws(ApiPieceOpError)]
-  fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId,
-        p: &dyn Piece, lens: &dyn Lens) -> PieceUpdateFromOp {
+  fn op(&self, a: ApiPieceOpArgs) -> PieceUpdateFromOp {
+    let ApiPieceOpArgs { gs,player,pst,piece,p,lens, .. } = a;
     '_normal_global_ops__not_loop: loop {
       let pc = gs.pieces.byid_mut(piece)?;
       let pl = gs.players.byid(player)?;
@@ -365,7 +379,7 @@ impl ApiPieceOp for ApiPieceUo {
             PieceUpdateOp::Modify(()),
             vec![ LogEntry { html: Html(format!(
               "{} flipped {}",
-              &htmlescape::encode_minimal(&pl.nick),
+              &htmlescape::encode_minimal(&pst.nick),
               p.describe_pri(&lens.log_pri(piece, pc)).0
             )) }])
         },
index fff1f6c28f3f18ec577713e803c70166d0161eb3..f32a174b58fda0986f59bab5fef257f09560b2ce 100644 (file)
@@ -100,7 +100,7 @@ pub enum MgmtGameResponse {
   PlayerAccessToken(Option<AccessTokenReport>),
 }
 
-#[derive(Debug,Serialize,Deserialize)]
+#[derive(Debug,Clone,Serialize,Deserialize)]
 pub struct AccessTokenReport {
   pub url: String,
 }
@@ -151,6 +151,7 @@ pub enum MgmtError {
   ZCoordinateOverflow(#[from] zcoord::Overflow),
   BadGlob { pat: String, msg: String },
   BadSpec(#[from] SpecError),
+  TokenDeliveryFailed(#[from] TokenDeliveryError),
 }
 impl Display for MgmtError {
   #[throws(fmt::Error)]
@@ -158,6 +159,8 @@ impl Display for MgmtError {
     use MgmtError::*;
     match self {
       ServerFailure(s) => write!(f, "ServerFailure: {}", &s)?,
+      TokenDeliveryFailed(tde) =>
+        write!(f, "access token delivery failed: {}", &tde)?,
       _ => <Self as Debug>::fmt(self,f)?,
     }
   }
index 1312d124482ff5076b4cdfc29cfdfb4a70cfd2c2..f50fd63f434ad5dfe071bbcdf08d5cf0e433b7d1 100644 (file)
@@ -47,6 +47,10 @@ pub enum InternalError {
   Anyhow(#[from] anyhow::Error),
 }
 
+#[derive(Error,Debug)]
+pub enum TokenDeliveryError {
+}
+
 impl From<InternalError> for SpecError {
   fn from(ie: InternalError) -> SpecError {
     SpecError::InternalError(format!("{:?}",ie))
index 5d8009982a2ca18edb5521b37d692e947a03aee1..9c3c6682e9bcd35e8ebf24eac8356c98e84c0655 100644 (file)
@@ -19,7 +19,7 @@ pub struct Generation (pub u64);
 
 visible_slotmap_key!{ VisiblePieceId('.') }
 
-#[derive(Clone,Serialize,Deserialize,Eq,Ord,PartialEq,PartialOrd)]
+#[derive(Clone,Serialize,Deserialize,Hash,Eq,Ord,PartialEq,PartialOrd)]
 #[serde(transparent)]
 pub struct Html (pub String);
 
@@ -198,7 +198,11 @@ impl ClampTable for Pos {
 }
 
 impl Html {
+  // todo convert to display_as but I need to write display_as::typed::Is
   pub fn lit(s: &str) -> Self { Html(s.to_owned()) }
+  pub fn from_txt(s: &str) -> Self {
+    Html(htmlescape::encode_minimal(&s))
+  }
 }
 
 impl Debug for Html {
index ef0fea8a7a472bf3aab990f7df5e9e9cbf1b91bf..77ff834970f0d721ce3869d01acaa077ade4d966 100644 (file)
@@ -45,7 +45,7 @@ pub struct PlayerRecord {
   pub pst: PlayerState,
 }
 
-#[derive(Debug,Serialize,Deserialize)]
+#[derive(Debug,Clone,Serialize,Deserialize)]
 pub struct PlayerState {
   pub account: AccountName,
   pub nick: String,
@@ -193,7 +193,7 @@ pub struct InstanceContainer {
 
 #[derive(Debug,Default,Serialize,Deserialize)]
 struct InstanceSaveAccesses<RawTokenStr, PiecesLoadedRef> {
-  pieces: PiecesLoadedRef,
+  ipieces: PiecesLoadedRef,
   tokens_players: Vec<(RawTokenStr, PlayerId)>,
   aplayers: SecondarySlotMap<PlayerId, PlayerState>,
 }
@@ -380,7 +380,7 @@ impl InstanceGuard<'_> {
     (||{
       self.save_game_now()?;
       self.save_access_now()?;
-      Ok(())
+      Ok::<_,InternalError>(())
     })().map_err(|e|{
       self.c.g.iplayers.remove(player);
       self.c.g.gs.players.remove(player);
@@ -397,14 +397,15 @@ impl InstanceGuard<'_> {
   //  #[throws(ServerFailure)]
   //  https://github.com/withoutboats/fehler/issues/62
   pub fn player_remove(&mut self, oldplayer: PlayerId)
-                       -> Result<Option<PlayerState>,InternalError> {
+                       -> Result<(Option<AccountName>, Option<PlayerState>),
+                                 InternalError> {
     // We have to filter this player out of everything
     // Then save
     // Then send updates
     // 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_data = players.remove(oldplayer);
+    let old_account = players.remove(oldplayer);
 
     // New state
     let mut gs = GameState {
@@ -457,7 +458,7 @@ impl InstanceGuard<'_> {
     // point of no return
     mem::drop(undo);
 
-    (||{
+    let old_pst = (||{
       for &piece in &updated_pieces {
         (||Some({
           self.c.g.gs.pieces.get_mut(piece)?.gen = self.c.g.gs.gen;
@@ -478,8 +479,9 @@ impl InstanceGuard<'_> {
         if remove { clients_to_remove.insert(k); }
         !remove
       });
-      if let Some(PlayerRecord { u: mut updates, .. })
-        = self.iplayers.remove(oldplayer) {
+      let pst = if let Some(PlayerRecord { u: mut updates, pst })
+        = self.iplayers.remove(oldplayer)
+      {
         updates.push(PreparedUpdate {
           gen: self.c.g.gs.gen,
           when: Instant::now(),
@@ -488,7 +490,10 @@ impl InstanceGuard<'_> {
             ErrorSignaledViaUpdate::PlayerRemoved
           )],
         });
-      }
+        Some(pst)
+      } else {
+        None
+      };
       self.tokens_deregister_for_id(|id:PlayerId| id==oldplayer);
       self.tokens_deregister_for_id(|id| clients_to_remove.contains(&id));
       self.save_access_now().unwrap_or_else(
@@ -496,9 +501,10 @@ impl InstanceGuard<'_> {
           "trouble garbage collecting accesses for deleted player: {:?}",
           &e)
       );
+      pst
     })(); // <- No ?, ensures that IEFE is infallible (barring panics)
 
-    Ok(old_data)
+    Ok((old_account, old_pst))
   }
 
   #[throws(MgmtError)]
@@ -506,8 +512,7 @@ impl InstanceGuard<'_> {
                                       player: PlayerId, token: RawToken,
                                       _safe: Authorised<RawToken>
   ) {
-    // xxx call this function when access changes
-
+    // xxx get rid of this or something ?
     self.tokens_deregister_for_id(|id:PlayerId| id==player);
     let iad = InstanceAccessDetails {
       gref : self.gref.clone(),
@@ -520,28 +525,31 @@ impl InstanceGuard<'_> {
   #[throws(MgmtError)]
   pub fn player_access_reset(&mut self, player: PlayerId)
                              -> Option<AccessTokenReport> {
-    let pst = self.c.g.players.get(player)
+    // xxx call this function when access changes
+
+    let pst = self.c.g.iplayers.get(player)
       .ok_or(MgmtError::PlayerNotFound)?
       .pst;
     self.save_access_now()?;
 
-    let access = {
-      let acct = AccountRecord::lookup_mut(&pst.account)?
-        .ok_or(MgmtError::AccountNotFound)?;
+    let access = AccountRecord::with_entry_mut(&pst.account, |acct|{
+      let acct = acct.ok_or(MgmtError::AccountNotFound)?;
       let access = acct.access;
       let desc = access.describe_html();
       let now = Timestamp::now();
-      access.entry(desc)
+      acct.tokens_revealed.entry(desc)
         .or_insert(TokenRevelation {
           latest: now,
           earliest: now,
         })
         .latest = now;
-      access.clone()
-    };
+      acct.expire_tokens_revealed();
+      Ok::<_,MgmtError>(access.clone())
+    }).map_err(|(e,_)|e)??;
 
     let token = access
       .override_token()
+      .cloned()
       .unwrap_or_else(||{
         RawToken::new_random()
         // xxx disconnect everyone else
@@ -554,10 +562,10 @@ impl InstanceGuard<'_> {
     self.token_register(token.clone(), iad);
 
     let report = AccessTokenReport {
-      url: format!("http://localhost:8000/{}", token.url), // xxx
+      url: format!("http://localhost:8000/{}", token.0), // xxx
     };
     let report = access
-      .server_deliver(&pst, &report);
+      .server_deliver(&pst, &report)?;
     report.cloned()
   }
 
@@ -712,7 +720,7 @@ impl InstanceGuard<'_> {
   #[throws(InternalError)]
   fn save_access_now(&mut self) {
     self.save_something("a-", |s,w| {
-      let pieces = &s.c.g.pieces;
+      let ipieces = &s.c.g.ipieces;
       let tokens_players : Vec<(&str, PlayerId)> = {
         let global_players = GLOBAL.players.read().unwrap();
         s.c.g.tokens_players.tr
@@ -725,9 +733,9 @@ impl InstanceGuard<'_> {
       };
       let aplayers = s.c.g.iplayers.iter().map(
         |(player, PlayerRecord { pst, .. })|
-        (player, pst)
+        (player, pst.clone())
       ).collect();
-      let isa = InstanceSaveAccesses { pieces, tokens_players, aplayers };
+      let isa = InstanceSaveAccesses { ipieces, tokens_players, aplayers };
       rmp_serde::encode::write_named(w, &isa)
     })?;
     self.c.access_dirty = false;
@@ -746,7 +754,7 @@ impl InstanceGuard<'_> {
   }
 
   #[throws(StartupError)]
-  fn load(name: InstanceName) -> InstanceRef {
+  fn load_game(name: InstanceName) -> InstanceRef {
     {
       let mut st = GLOBAL.save_area_lock.lock().unwrap();
       let st = &mut *st;
@@ -791,15 +799,15 @@ impl InstanceGuard<'_> {
     let iplayers = {
       let a = aplayers;
       a.drain()
-    }.map(|pst| {
+    }.map(|(player, pst)| {
       let u = pu_bc.new();
-      PlayerRecord { u, pst }
+      (player, PlayerRecord { u, pst })
     }).collect();
 
     for mut p in gs.pieces.values_mut() {
       p.lastclient = Default::default();
       if let Some(held) = p.held {
-        if !gs.players.has_key(held) { p.held = None }
+        if !gs.players.contains_key(held) { p.held = None }
       }
     }
 
@@ -867,7 +875,7 @@ pub fn load_games() {
           );
         },
         GameFile { access_leaf, name } => {
-          InstanceGuard::load(name)?;
+          InstanceGuard::load_game(name)?;
           a_leaves.insert(access_leaf, Used);
         },
       }
index 3c8a9213a3dba2821ce245e4d3a361584ca60d9b..b8821566fa1bce69fe4da063a58ac2fde07c12d2 100644 (file)
@@ -12,6 +12,7 @@ pub use std::thread::{self,sleep};
 pub use std::time::Duration;
 pub use std::sync::{Arc,Mutex,MutexGuard,RwLock,RwLockReadGuard,Condvar};
 pub use std::collections::{HashMap,hash_map,HashSet};
+pub use std::hash::Hash;
 pub use std::borrow::Borrow;
 pub use std::convert::{TryFrom,TryInto};
 pub use std::str;
index 915692f1c51ac2fd244c4481c33327faf29cf11c..188f210b0ff39e5aea5e2a638f14d23e98f0719f 100644 (file)
@@ -94,7 +94,8 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
     }
 
     let pl = ig.gs.players.byid_mut(player)?;
-    let tz = &ig.updates.byid(player)?.tz;
+    let ipl = ig.iplayers.byid(player)?;
+    let tz = &ipl.pst.tz;
     let mut pieces : Vec<_> = ig.gs.pieces.iter().collect();
 
     pieces.sort_by_key(|(_,pr)| &pr.zlevel);
@@ -104,7 +105,7 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
         id : make_pieceid_visible(gpid),
         face : pr.face,
       };
-      let p = if let Some(p) = ig.pieces.get(gpid) { p }
+      let p = if let Some(p) = ig.ipieces.get(gpid) { p }
       else { continue /* was deleted */ };
       let defs = p.make_defs(&pri)?;
       alldefs.push((pri.id, defs));
@@ -137,10 +138,11 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
       player,
       defs : alldefs,
       uses,
-      nick : pl.nick.clone(),
+      nick : ipl.pst.nick.clone(),
       load : serde_json::to_string(&DataLoad {
         players : load_players,
       }).map_err(|e| InternalError::JSONEncode(e))?,
+      // xxx show account accesses
     };
     trace!("SessionRenderContext {:?}", &src);
     (src, client)
index 568b53de6b4c775b3591e09beca0d73e1e1904a0..ab520b59f9e3045e2b8e2150badb5380d976f75e 100644 (file)
@@ -13,6 +13,7 @@ use std::collections::hash_set::HashSet;
 use thiserror::Error;
 use crate::error::display_as_debug;
 use crate::accounts::AccountName;
+use std::hash::Hash;
 
 pub use implementation::PlayerAccessSpec;
 
@@ -65,13 +66,14 @@ pub struct TableSpec {
 pub type Acl<Perm> = Vec<AclEntry<Perm>>;
 
 #[derive(Debug,Clone,Serialize,Deserialize)]
-pub struct AclEntry<Perm> {
+pub struct AclEntry<Perm: Eq + Hash> {
   pub account_glob: String,
   pub allow: HashSet<Perm>,
   pub deny: HashSet<Perm>,
 }
 
 #[derive(Debug,Clone,Copy,Serialize,Deserialize)]
+#[derive(Hash,Eq,PartialEq,Ord,PartialOrd)]
 enum TablePermission {
   AddPlayer,
   ChangePieces,
@@ -221,6 +223,8 @@ pub mod implementation {
   use crate::imports::*;
   type Insn = crate::commands::MgmtGameInstruction;
 
+  type TDE = TokenDeliveryError;
+
   pub fn raw_token_debug_as_str(s: &str, f: &mut fmt::Formatter)
                                 -> fmt::Result {
     let len = min(5, s.len() / 2);
@@ -243,13 +247,17 @@ pub mod implementation {
       None
     }
     fn server_deliver(&self, pst: &PlayerState, token: &AccessTokenReport)
-                      -> Result<Option<&AccessTokenReport>, AE> {
+                      -> Result<Option<&AccessTokenReport>, TDE> {
       Ok(None)
     }
     fn client_deliver(&self, pst: &PlayerState, token: &AccessTokenReport)
-                      -> Result<(),AE> {
+                      -> Result<(), TDE> {
       panic!()
     }
+    fn describe_html(&self) -> Html {
+      let inner = Html::from_txt(&format!("{:?}", self));
+      Html(format!("<code>{}</code>", inner.0))
+    }
   }
 
   #[typetag::serde]
@@ -259,7 +267,7 @@ pub mod implementation {
   #[typetag::serde]
   impl PlayerAccessSpec for FixedToken {
     fn override_token(&self) -> Option<&RawToken> {
-      Some(self.token)
+      Some(&self.token)
     }
     fn client_mgi(&self, player: PlayerId) -> Option<MgmtGameInstruction> {
       Some(Insn::SetFixedPlayerAccess {
@@ -274,12 +282,12 @@ pub mod implementation {
     fn client_mgi(&self, player: PlayerId) -> Option<MgmtGameInstruction> {
       Some(Insn::ReportPlayerAccesses(player))
     }
-    #[throws(AE)]
+    #[throws(TDE)]
     fn server_deliver(&self, ps: &PlayerState, token: &AccessTokenReport)
                       -> Option<&AccessTokenReport> {
       Some(token)
     }
-    #[throws(AE)]
+    #[throws(TDE)]
     fn client_deliver(&self, ps: &PlayerState, token: &AccessTokenReport) {
       println!("access account={} nick={:?} url:\n{}",
                &ps.account, &ps.nick, token.url);
index 1db533ef2d1316ae835982fb2e20b77f8e36fafe..e99e71954765025e83421f2586aab56bdfe6a839 100644 (file)
@@ -86,8 +86,9 @@ impl Read for UpdateReader {
              self.player, self.client, ig.gs.gen, self.to_send)?;
     }
 
-    let pu = &mut ig.updates.get_mut(self.player)
+    let iplayer = &mut ig.iplayers.get_mut(self.player)
       .ok_or_else(|| self.trouble("player gonee",()))?;
+    let pu = &mut iplayer.u;
 
     loop {
       if let Some(ref mut overflow) = self.overflow {
@@ -124,7 +125,7 @@ impl Read for UpdateReader {
         }
         self.overflow = {
           let mut overflow = Vec::with_capacity(next_len);
-          self.wn.write_next(&mut overflow, &pu.tz, &next)
+          self.wn.write_next(&mut overflow, &iplayer.pst.tz, &next)
             .map_err(|e| self.wn.trouble("overflow.write_next",&e))?;
           debug!("overflow {} {}, len={}",
                  &self.wn.player, &self.wn.client, &overflow.len());
@@ -133,7 +134,7 @@ impl Read for UpdateReader {
         continue;
       }
 
-      self.wn.write_next(&mut buf, &pu.tz, &next)
+      self.wn.write_next(&mut buf, &iplayer.pst.tz, &next)
         .map_err(|e| self.wn.trouble("UpdateReader.write_next",&e))?;
 
       let before = next.when - UPDATE_EXPIRE;
index da095aaf8399353c5887dbc57a3292dc2b16d9eb..c6ee500bf588bf9765c0b635846d6a0b7b505162 100644 (file)
@@ -346,7 +346,7 @@ impl<'r> PrepareUpdatesBuffer<'r> {
 
     let (update, piece) = match (
       gs.pieces.byid_mut(piece),
-      self.g.pieces.get(piece),
+      self.g.ipieces.get(piece),
     ) {
       (Ok(pc), Some(p)) => {
         gs.max_z.update_max(&pc.zlevel.z);
@@ -435,7 +435,9 @@ impl<'r> Drop for PrepareUpdatesBuffer<'r> {
       let update = Arc::new(update);
       trace!("PrepareUpdatesBuffer update {:?}", &update);
 
-      for (_tplayer, tplupdates) in &mut self.g.updates {
+      for (_tplayer, PlayerRecord { u: tplupdates, .. })
+        in &mut self.g.iplayers
+      {
         tplupdates.push(update.clone());
       }
     }