chiark / gitweb /
wip adjust players etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 1 Nov 2020 21:21:48 +0000 (21:21 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 1 Nov 2020 21:21:48 +0000 (21:21 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/api.rs
src/cmdlistener.rs
src/commands.rs
src/error.rs
src/global.rs
src/spec.rs

index 2d39e9ff725e95d9c45741cf7266a282dd128f4d..787c01e4a39b515609b51f4bf706275e952894b5 100644 (file)
@@ -44,6 +44,12 @@ pub enum ApiPieceOpError {
 }
 display_as_debug!(ApiPieceOpError);
 
+impl From<PlayerNotFound> for ApiPieceOpError {
+  fn from(x: PlayerNotFound) -> ApiPieceOpError {
+    ApiPieceOpError::ReportViaResponse(x.into())
+  }
+}
+
 pub trait Lens : Debug {
   fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId;
   fn log_pri(&self, piece: PieceId, pc: &PieceState)
@@ -89,7 +95,7 @@ impl<'r> Responder<'r> for OnlineError {
     use OnlineError::*;
     let status = match self {
       ServerFailure(_) => Status::InternalServerError,
-      NoClient | NoPlayer | GameBeingDestroyed
+      NoClient | NoPlayer(_) | GameBeingDestroyed
         => Status::NotFound,
       OnlineError::PieceHeld | OnlineError::PieceGone
         => Status::Conflict,
index 51dca15f3148388856aa0e1883149e44fe7cded0..75aaa825a294f6702fbe59f087e2b76d3f2ee047 100644 (file)
@@ -233,6 +233,13 @@ fn execute_game_insn<'ig>(
   type Resp = MgmtGameResponse;
   let who = &cs.who; // todo show player nick when it's a player
 
+  fn tz_from_str(s: &str) -> Timezone {
+    match Timezone::from_str(s) {
+      Ok(tz) => tz,
+      Err(x) => match x { },
+    }
+  }
+
   #[throws(MgmtError)]
   fn readonly<'ig,
               F: FnOnce(&InstanceGuard) -> Result<MgmtGameResponse,ME>,
@@ -299,10 +306,7 @@ fn execute_game_insn<'ig>(
       };
       let timezone = timezone.as_ref().map(String::as_str)
         .unwrap_or("");
-      let tz = match Timezone::from_str(timezone) {
-        Ok(tz) => tz,
-        Err(x) => match x { },
-      };
+      let tz = tz_from_str(&timezone);
       let gpl = GPlayerState {
         nick: nick.to_string(),
       };
@@ -326,16 +330,17 @@ fn execute_game_insn<'ig>(
         let itemname = pinfo.itemname().to_string();
         let bbox = pinfo.bbox_approx();
         let lens = TransparentLens { };
+        #[allow(irrefutable_let_patterns)]
+        let visible = if let TransparentLens { } = lens {
+          Some(MgmtGamePieceVisibleInfo {
+            pos, face, desc_html, bbox
+          })
+        } else {
+          None
+        };
         Some(MgmtGamePieceInfo {
           piece, itemname,
-          visible:
-          if let TransparentLens { } = lens {
-            Some(MgmtGamePieceVisibleInfo {
-              pos, face, desc_html, bbox
-            })
-          } else {
-            None
-          }
+          visible
         })
       }).flatten().collect();
       Ok(Resp::Pieces(pieces))
@@ -358,6 +363,34 @@ fn execute_game_insn<'ig>(
        Fine, ig)
     },
 
+    UpdatePlayer {
+      player,
+      details: MgmtPlayerDetails { nick, timezone },
+    } => {
+      let ig = cs.check_acl_modify_player(ag, ig, player,
+                                          &[TP::ModifyOtherPlayer])?.0;
+      let ipr = ig.iplayers.byid_mut(player)?;
+      let gpl = ig.gs.players.byid_mut(player)?;
+      let mut log = vec![];
+      if let Some(new_nick) = nick {
+        ig.check_new_nick(&new_nick)?;
+        log.push(LogEntry {
+          html: Html(format!("{} changed {}'s nick to {}",
+                             &who,
+                             htmlescape::encode_minimal(&gpl.nick),
+                             htmlescape::encode_minimal(&new_nick))),
+        });
+        gpl.nick = new_nick;
+      }
+      if let Some(new_timezone) = timezone {
+        ipr.ipl.tz = tz_from_str(&new_timezone);
+      }
+      (U{ log,
+          pcs: vec![],
+          raw: None},
+       Fine, ig)
+    },
+
     Insn::Info => readonly(cs,ag,ig, &[TP::ViewPublic], |ig|{
       let players = ig.gs.players.iter().map(
         |(player, &gpl)| {
@@ -734,8 +767,7 @@ impl CommandStream<'_> {
   {
     let (ipl_unauth, gpl_unauth) = {
       let ig = ig.by(Authorisation::authorise_any());
-      (ig.iplayers.get(player).ok_or(ME::PlayerNotFound)?,
-       ig.gs.players.get(player).ok_or(ME::PlayerNotFound)?)
+      (ig.iplayers.byid(player)?, ig.gs.players.byid(player)?)
     };
     let how = PCH::InstanceOrOnlyAffectedAccount(ipl_unauth.ipl.acctid);
     let (ig, auth) = self.check_acl(ag, ig, how, p)?;
@@ -801,6 +833,7 @@ impl CommandStream<'_> {
             else { None }
           }
         },
+        PCH::Instance => None,
       } {
         return auth;
       }
index a7cdaf8b036c0b8eed811948c106715551c0655f..eec60177a28c9549e962724d489ef65ecd9c23f9 100644 (file)
@@ -153,7 +153,7 @@ pub enum MgmtError {
   GameNotFound,
   GameCorrupted,
   AccountNotFound(#[from] AccountNotFound),
-  PlayerNotFound,
+  PlayerNotFound(#[from] PlayerNotFound),
   AuthorisationUninitialised,
   PieceNotFound,
   LimitExceeded,
index 654ad8a358bdb0db20aba3b0aa13f70bcee76145..e419ea5476e0f1490f9076e5dee52c194f46ac75 100644 (file)
@@ -11,7 +11,7 @@ pub enum OnlineError {
   #[error("client session not recognised (terminated by server?)")]
   NoClient,
   #[error("player not part of game (removed?)")]
-  NoPlayer,
+  NoPlayer(#[from] PlayerNotFound),
   #[error("invalid Z coordinate")]
   InvalidZCoord,
   #[error("Server operational problems - consult administrator: {0:?}")]
@@ -140,8 +140,8 @@ some_slotmap!{DenseSlotMap}
 some_slotmap!{SecondarySlotMap}
 
 impl<T> IdForById for T where T : AccessId {
-  type Error = OE;
-  const ERROR : OE = <Self as AccessId>::ERROR;
+  type Error = T::Error;
+  const ERROR : Self::Error = <Self as AccessId>::ERROR;
 }
 
 impl IdForById for PieceId {
index 6ff438c944191c460087446860429feae859fbb2..b729ccc3b09c232ea71ba1e1ac649f946cd20eee 100644 (file)
@@ -433,9 +433,7 @@ impl<'ig> InstanceGuard<'ig> {
                     logentry: LogEntry) -> (PlayerId, LogEntry) {
     // saving is fallible, but we can't attempt to save unless
     // we have a thing to serialise with the player in it
-    if self.c.g.gs.players.values().any(|old| old.nick == gnew.nick) {
-      Err(MgmtError::NickCollision)?;
-    }
+    self.check_new_nick(&gnew.nick)?;
     if self.c.g.iplayers.values().any(|r| r.ipl.acctid == inew.acctid) {
       Err(MgmtError::AlreadyExists)?;
     }
@@ -461,6 +459,13 @@ impl<'ig> InstanceGuard<'ig> {
     (player, logentry)
   }
 
+  #[throws(MgmtError)]
+  pub fn check_new_nick(&mut self, new_nick: &str) {
+    if self.c.g.gs.players.values().any(|old| old.nick == new_nick) {
+      Err(MgmtError::NickCollision)?;
+    }
+  }
+
   pub fn remove_clients(&mut self,
                         player: PlayerId,
                         signal: ErrorSignaledViaUpdate) {
@@ -606,11 +611,8 @@ impl<'ig> InstanceGuard<'ig> {
                                    authorised: Authorisation<AccountName>,
                                    reset: bool)
                                    -> Option<AccessTokenReport> {
-    let ipl = self.c.g.iplayers.get(player)
-      .ok_or(MgmtError::PlayerNotFound)?
-      .ipl;
-    let gpl = self.c.g.gs.players.get(player)
-      .ok_or(MgmtError::PlayerNotFound)?;
+    let ipl = self.c.g.iplayers.byid(player)?.ipl;
+    let gpl = self.c.g.gs.players.byid(player)?;
 
     let (access, acctid) = accounts.with_entry_mut(
       ipl.acctid, authorised, None,
@@ -1026,14 +1028,22 @@ pub fn load_games(accounts: &mut AccountsGuard) {
 pub type TokenTable<Id> = HashMap<RawToken, InstanceAccessDetails<Id>>;
 
 pub trait AccessId : Copy + Clone + 'static {
+  type Error : Into<OnlineError>;
+  const ERROR : Self::Error;
   fn global_tokens(_:PrivateCaller) -> &'static RwLock<TokenTable<Self>>;
   fn tokens_registry(ig: &mut Instance, _:PrivateCaller)
                      -> &mut TokenRegistry<Self>;
-  const ERROR : OnlineError;
 }
 
+#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd)]
+#[derive(Serialize,Deserialize)]
+#[derive(Error)]
+#[error("Player not found")]
+pub struct PlayerNotFound;
+
 impl AccessId for PlayerId {
-  const ERROR : OnlineError = NoPlayer;
+  type Error = PlayerNotFound;
+  const ERROR : PlayerNotFound = PlayerNotFound;
   fn global_tokens(_: PrivateCaller) -> &'static RwLock<TokenTable<Self>> {
     &GLOBAL.players
   }
@@ -1043,6 +1053,7 @@ impl AccessId for PlayerId {
   }
 }
 impl AccessId for ClientId {
+  type Error = OnlineError;
   const ERROR : OnlineError = NoClient;
   fn global_tokens(_: PrivateCaller) -> &'static RwLock<TokenTable<Self>> {
     &GLOBAL.clients
@@ -1065,13 +1076,13 @@ impl RawToken {
 }
 
 pub fn lookup_token<Id : AccessId>(s : &RawTokenVal)
-      -> Result<InstanceAccessDetails<Id>, OE> {
+      -> Result<InstanceAccessDetails<Id>, Id::Error> {
   Id::global_tokens(PRIVATE_Y).read().unwrap().get(s).cloned()
     .ok_or(Id::ERROR)
 }
 
 impl<'r, Id> FromParam<'r> for InstanceAccess<'r, Id>
-  where Id : AccessId
+  where Id : AccessId, OE : From<Id::Error>
 {
   type Error = OE;
   #[throws(OE)]
index 964affe4e0ca82dec3332115fe141fd19f7f3ffc..e620d3faa1db541526529305d4227bffe8d49eed 100644 (file)
@@ -88,6 +88,7 @@ pub enum TablePermission {
   ChangePieces,
   ResetOthersAccess,
   RedeliverOthersAccess,
+  ModifyOtherPlayer,
   RemovePlayer,
   ChangeACL,
 }