From 1cb5f94be1d861e6db65df7bb22661af972bceff Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sun, 1 Nov 2020 21:21:48 +0000 Subject: [PATCH] wip adjust players etc. Signed-off-by: Ian Jackson --- src/api.rs | 8 +++++- src/cmdlistener.rs | 61 +++++++++++++++++++++++++++++++++++----------- src/commands.rs | 2 +- src/error.rs | 6 ++--- src/global.rs | 35 +++++++++++++++++--------- src/spec.rs | 1 + 6 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/api.rs b/src/api.rs index 2d39e9ff..787c01e4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -44,6 +44,12 @@ pub enum ApiPieceOpError { } display_as_debug!(ApiPieceOpError); +impl From 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, diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 51dca15f..75aaa825 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -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, @@ -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; } diff --git a/src/commands.rs b/src/commands.rs index a7cdaf8b..eec60177 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -153,7 +153,7 @@ pub enum MgmtError { GameNotFound, GameCorrupted, AccountNotFound(#[from] AccountNotFound), - PlayerNotFound, + PlayerNotFound(#[from] PlayerNotFound), AuthorisationUninitialised, PieceNotFound, LimitExceeded, diff --git a/src/error.rs b/src/error.rs index 654ad8a3..e419ea54 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 IdForById for T where T : AccessId { - type Error = OE; - const ERROR : OE = ::ERROR; + type Error = T::Error; + const ERROR : Self::Error = ::ERROR; } impl IdForById for PieceId { diff --git a/src/global.rs b/src/global.rs index 6ff438c9..b729ccc3 100644 --- a/src/global.rs +++ b/src/global.rs @@ -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, reset: bool) -> Option { - 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 = HashMap>; pub trait AccessId : Copy + Clone + 'static { + type Error : Into; + const ERROR : Self::Error; fn global_tokens(_:PrivateCaller) -> &'static RwLock>; fn tokens_registry(ig: &mut Instance, _:PrivateCaller) -> &mut TokenRegistry; - 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> { &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> { &GLOBAL.clients @@ -1065,13 +1076,13 @@ impl RawToken { } pub fn lookup_token(s : &RawTokenVal) - -> Result, OE> { + -> Result, 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 { type Error = OE; #[throws(OE)] diff --git a/src/spec.rs b/src/spec.rs index 964affe4..e620d3fa 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -88,6 +88,7 @@ pub enum TablePermission { ChangePieces, ResetOthersAccess, RedeliverOthersAccess, + ModifyOtherPlayer, RemovePlayer, ChangeACL, } -- 2.30.2