From: Ian Jackson Date: Sun, 27 Sep 2020 22:45:32 +0000 (+0100) Subject: new UI operations arrangements X-Git-Tag: otter-0.2.0~825 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=84c9f3ee3fe9bb00e6d4b9e5bb48e882476abe4d;p=otter.git new UI operations arrangements Signed-off-by: Ian Jackson --- diff --git a/src/api.rs b/src/api.rs index 13bf4c7d..653d7141 100644 --- a/src/api.rs +++ b/src/api.rs @@ -265,11 +265,59 @@ impl ApiPieceOp for ApiPieceMove { } } +const DEFKEY_FLIP : UoKey = 'f'; + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPieceUo (UoKey); +#[post("/_/api/k", format="json", data="
")] +#[throws(OE)] +fn api_uo(form : Json>) -> impl response::Responder<'static> { + api_piece_op(form) +} +impl ApiPieceOp for ApiPieceUo { + #[throws(ApiPieceOpError)] + fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, + p: &dyn Piece, lens: &dyn Lens) + -> (PieceUpdateOp<()>, Vec) { + let def_key = self.0; + + '_normal_global_ops__not_loop: loop { + let pc = gs.pieces.byid_mut(piece)?; + let pl = gs.players.byid(player)?; + let _: Impossible = match def_key { + + DEFKEY_FLIP => { + let nfaces = p.nfaces(); + pc.face = (RawFaceId::from(pc.face) % nfaces).into(); + return (PieceUpdateOp::Modify(()), + vec![ LogEntry { html: Html(format!( + "{} flipped {}", + &htmlescape::encode_minimal(&pl.nick), + p.describe_pri(&lens.log_pri(piece, pc)).0 + )) }]) + }, + + _ => break, + }; + } + + '_abnormal_global_ops__notloop: loop { + let _: Impossible = match def_key { + + _ => break, + }; + } + + p.ui_operation(gs, player, piece, def_key, lens)? + } +} + pub fn mount(rocket_instance: Rocket) -> Rocket { rocket_instance.mount("/", routes![ api_grab, api_ungrab, api_raise, api_move, + api_uo, ]) } diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 99e59c62..95d93170 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -265,7 +265,10 @@ fn execute_game_insn(cs: &CommandStream, let mut pos = pos.unwrap_or(DEFAULT_POS_START); for i in 0..count { let p = info.load()?; - let face = p.resolve_spec_face(face)?; + let face = face.unwrap_or_default(); + if p.nfaces() <= face.into() { + throw!(SpecError::FaceNotFound); + } let z = ZCoord(gs.max_z.0 + (i + 1) as f64); let pc = PieceState { held: None, diff --git a/src/gamestate.rs b/src/gamestate.rs index 2caf94ad..e4ee9214 100644 --- a/src/gamestate.rs +++ b/src/gamestate.rs @@ -90,43 +90,26 @@ pub trait Outline : Send + Debug { pub type UoKey = char; +#[derive(Debug,Clone,Serialize,Deserialize)] pub struct UoDescription { pub def_key: UoKey, pub opname: String, pub desc: Html, } -impl UoDescription { - pub fn new_flip() -> UoDescription { UoDescription { - def_key: 'f'.into(), - opname: "flip".to_string(), - desc: Html::lit("flip"), - } } -} - -#[throws(ApiPieceOpError)] -pub fn ui_operation_flip(gs: &mut GameState, player: PlayerId, - piece: PieceId, lens: &dyn Lens, - def_key: UoKey, nfaces: RawFaceId) - -> PieceUpdateFromOp { - let pl = gs.players.byid(player)?; - let pc = gs.pieces.byid_mut(piece)?; - pc.face = (pc.face.into::() % nfaces).into(); - (PieceUpdateOp::Modify(()), LogEntry { html: Html(format!( - "{} flipped {}", - &htmlescape::encode_minimal(&pl.nick), - pc.describe_pri(&lens.log_pri(piece, pc)).0))}) -} - #[typetag::serde] pub trait Piece : Outline + Send + Debug { - fn resolve_spec_face(&self, face : Option) - -> Result; - fn ui_operations<'p>(&'p self) -> Result +'p - >>, IE>; - fn ui_operation(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, - def_key: char, lens: &dyn Lens) -> PieceUpdateResult; + fn nfaces(&self) -> RawFaceId; + + #[throws(InternalError)] + fn add_ui_operations(&self, _upd: &mut Vec) { } + + fn ui_operation(&self, + _gs: &mut GameState, _player: PlayerId, _piece: PieceId, + _def_key: char, _lens: &dyn Lens) + -> PieceUpdateResult { + throw!(OE::BadOperation) + } // #[throws] doesn't work here for some reason fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) -> IR; @@ -243,6 +226,7 @@ impl PieceState { svg : p.make_defs(pri)?, z : self.zlevel.z, zg : self.zlevel.zg, + uos : p.ui_operations()?, } } } @@ -250,6 +234,7 @@ impl PieceState { pub trait PieceExt { fn make_defs(&self, pri : &PieceRenderInstructions) -> Result; fn describe_pri(&self, pri : &PieceRenderInstructions) -> Html; + fn ui_operations(&self) -> Result, IE>; } impl PieceExt for T where T: Piece + ?Sized { @@ -276,6 +261,20 @@ impl PieceExt for T where T: Piece + ?Sized { fn describe_pri(&self, pri : &PieceRenderInstructions) -> Html { self.describe_html(Some(pri.face)) } + + #[throws(InternalError)] + fn ui_operations(&self) -> Vec { + let mut out = vec![]; + if self.nfaces() > 1 { + out.push(UoDescription { + def_key: 'f'.into(), + opname: "flip".to_string(), + desc: Html::lit("flip"), + }) + } + self.add_ui_operations(&mut out)?; + out + } } // ========== ad-hoc and temporary ========== diff --git a/src/global.rs b/src/global.rs index cee4af1f..469b0d49 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1051,7 +1051,6 @@ pub fn client_expire_old_clients() { } for gref in expire.drain(..) { struct Now(HashSet); - enum Impossible { } impl ClientIterator for Now { type Ret = Impossible; fn old(&mut self, client: ClientId) diff --git a/src/imports.rs b/src/imports.rs index fa6d610b..9cdcaed4 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -115,6 +115,7 @@ pub use crate::shapelib; pub use nix::unistd::Uid; pub fn default() -> T { Default::default() } +pub enum Impossible { } pub type E = anyhow::Error; pub type AE = anyhow::Error; diff --git a/src/pieces.rs b/src/pieces.rs index 77528f6f..3c09b729 100644 --- a/src/pieces.rs +++ b/src/pieces.rs @@ -136,31 +136,7 @@ impl Piece for SimpleShape { format!("a {}", self.desc.0) }) } - #[throws(SpecError)] - fn resolve_spec_face(&self, face: Option) -> FaceId { - let face = face.unwrap_or_default(); - self.colours.get(face).ok_or(SpecError::FaceNotFound)?; - face - } - - #[throws(IE)] - fn ui_operations(&self) -> Box> { - if self.colours.len() > 1 { - Box::new(iter::once(UoDescription::new_flip())) - } else { - Box::new(iter::empty()) - } - } - - #[throws(ApiPieceOpError)] - fn ui_operation(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, - def_key: UoKey, lens: &dyn Lens) -> PieceUpdateFromOp { - if let Some(got) = - ui_operation_flip(gs,player,piece,def_key,lens, - self.colours().into())? { return got } - throw!(OE::BadOperation) - } - + fn nfaces(&self) -> RawFaceId { self.colours.len().try_into().unwrap() } fn itemname(&self) -> &str { &self.itemname } } diff --git a/src/shapelib.rs b/src/shapelib.rs index d80b06df..075e472d 100644 --- a/src/shapelib.rs +++ b/src/shapelib.rs @@ -158,12 +158,7 @@ impl Outline for Item { delegate! { to self.outline { #[typetag::serde(name="Lib")] impl Piece for Item { - #[throws(SpecError)] - fn resolve_spec_face(&self, face: Option) -> FaceId { - let face = face.unwrap_or_default(); - self.faces.get(face).ok_or(SpecError::FaceNotFound)?; - face - } + fn nfaces(&self) -> RawFaceId { self.faces.len().try_into().unwrap() } #[throws(IE)] fn svg_piece(&self, f: &mut Html, pri: &PieceRenderInstructions) { diff --git a/src/spec.rs b/src/spec.rs index 332c3cb3..e9359080 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -31,6 +31,7 @@ pub type RawFaceId = u8; define_index_type! { #[derive(Default)] pub struct FaceId = RawFaceId; + IMPL_RAW_CONVERSIONS = true; } #[derive(Serialize,Deserialize)] diff --git a/src/updates.rs b/src/updates.rs index 359d6bed..a46e8456 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -59,6 +59,7 @@ pub struct PreparedPieceState { pub held : Option, pub z : ZCoord, pub zg : Generation, + pub uos: Vec, } // ---------- piece updates ----------