From: Ian Jackson Date: Tue, 19 Apr 2022 18:58:32 +0000 (+0100) Subject: Introduce OpOutcomeThunk X-Git-Tag: otter-1.1.0~428 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=3352cbcf3690590c1bfde28587b8a50b0f3565e6;p=otter.git Introduce OpOutcomeThunk Signed-off-by: Ian Jackson --- diff --git a/daemon/api.rs b/daemon/api.rs index ccd3b55c..e44bea8e 100644 --- a/daemon/api.rs +++ b/daemon/api.rs @@ -59,13 +59,13 @@ mod op { pub trait Complex: Core + Debug { #[throws(ApiPieceOpError)] - fn op_complex(&self, a: ApiPieceOpArgs) -> UpdateFromOpComplex; + fn op_complex(&self, a: ApiPieceOpArgs) -> OpOutcomeThunk; } impl Complex for T where T: Core + Simple { #[throws(ApiPieceOpError)] - fn op_complex(&self, a: ApiPieceOpArgs) -> UpdateFromOpComplex { - (self.op(a)?, None) + fn op_complex(&self, a: ApiPieceOpArgs) -> OpOutcomeThunk { + (self.op(a)?, None).into() } } } @@ -142,7 +142,12 @@ fn api_piece_op(form: Json>) to_recalculate: &mut to_recalculate, })?; Ok::<_,ApiPieceOpError>((update, loose_conflict)) - })() { + })().and_then(|(thunk, loose_conflict)| Ok(( + match thunk { + OpOutcomeThunk::Immediate(r) => r, + OpOutcomeThunk::Reborrow(f) => f(g, player, piece)?, + }, loose_conflict + ))) { Err(APOE::Inapplicable(poe)) => { PrepareUpdatesBuffer::piece_report_error( &mut ig, poe, @@ -569,13 +574,15 @@ api_route!{ n: MultigrabQty, } - as: - #[throws(ApiPieceOpError)] - fn op(&self, mut a: ApiPieceOpArgs) -> PieceUpdate { - if ! a.ipc.special.multigrab { throw!(Ia::BadPieceStateForOperation) } - let pri = a.pri()?; - let y = pri.fully_visible().ok_or(Ia::Occultation)?; - a.ipc.show(y).op_multigrab(a, pri, self.n)? + impl op::Core as { } + impl op::Complex as { + #[throws(ApiPieceOpError)] + fn op_complex(&self, mut a: ApiPieceOpArgs) -> OpOutcomeThunk { + if ! a.ipc.special.multigrab { throw!(Ia::BadPieceStateForOperation) } + let pri = a.pri()?; + let y = pri.fully_visible().ok_or(Ia::Occultation)?; + a.ipc.show(y).op_multigrab(a, pri, self.n)? + } } } @@ -589,7 +596,7 @@ api_route!{ impl op::Core as { } impl op::Complex as { #[throws(ApiPieceOpError)] - fn op_complex(&self, mut a: ApiPieceOpArgs) -> UpdateFromOpComplex { + fn op_complex(&self, mut a: ApiPieceOpArgs) -> OpOutcomeThunk { let pri = a.pri()?; let ApiPieceOpArgs { ioccults,player,piece,ipc, .. } = a; let gs = &mut a.gs; @@ -615,7 +622,7 @@ api_route!{ wrc, PieceUpdateOp::Modify(()), logents, - ).into(), None) + ).into(), None).into() }, _ => break, diff --git a/src/clock.rs b/src/clock.rs index b8c49bc9..256954d9 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -611,7 +611,7 @@ impl PieceTrait for Clock { #[throws(ApiPieceOpError)] fn ui_operation(&self, _: ShowUnocculted, args: ApiPieceOpArgs<'_>, opname: &str, _wrc: WhatResponseToClientOp) - -> UpdateFromOpComplex { + -> OpOutcomeThunk { let ApiPieceOpArgs { gs,piece,player,ioccults,ipc,ig,.. } = args; let gpc = gs.pieces.byid_mut(piece)?; let held = gpc.held; @@ -708,7 +708,7 @@ impl PieceTrait for Clock { ); r } - } + }.into() } #[throws(IE)] diff --git a/src/deck.rs b/src/deck.rs index 006af8d0..15328ef5 100644 --- a/src/deck.rs +++ b/src/deck.rs @@ -173,7 +173,7 @@ impl PieceTrait for Deck { fn ui_operation(&self, vis: ShowUnocculted, a: ApiPieceOpArgs<'_>, opname: &str, wrc: WhatResponseToClientOp) - -> UpdateFromOpComplex { + -> OpOutcomeThunk { let ApiPieceOpArgs { gs,player,piece,ipieces,ioccults,to_recalculate,.. } = a; let gen = &mut gs.gen; let gplayers = &mut gs.players; @@ -247,7 +247,7 @@ impl PieceTrait for Deck { (PieceUpdate { wrc, log, ops: puos.into(), - }, xupdates.into_unprepared(None)) + }, xupdates.into_unprepared(None)).into() } fn occultation_notify_hook(&self, piece: PieceId) -> UnpreparedUpdates { diff --git a/src/dice.rs b/src/dice.rs index 5026eaad..09db3320 100644 --- a/src/dice.rs +++ b/src/dice.rs @@ -371,7 +371,7 @@ impl PieceTrait for Die { #[throws(ApiPieceOpError)] fn ui_operation(&self, _: ShowUnocculted, args: ApiPieceOpArgs<'_>, opname: &str, wrc: WhatResponseToClientOp) - -> UpdateFromOpComplex { + -> OpOutcomeThunk { let ApiPieceOpArgs { gs,piece,player,ioccults,ipc,.. } = args; let gpc = gs.pieces.byid_mut(piece)?; let gpl = gs.players.byid(player)?; @@ -392,7 +392,7 @@ impl PieceTrait for Die { wrc, PieceUpdateOp::Modify(()), logents, - ).into(), None) + ).into(), None).into() }, _ => throw!(Ia::BadUiOperation) diff --git a/src/gamestate.rs b/src/gamestate.rs index 523eb8a8..98e41037 100644 --- a/src/gamestate.rs +++ b/src/gamestate.rs @@ -199,7 +199,7 @@ pub trait PieceTrait: PieceBaseTrait + Send + Debug + 'static { fn ui_operation(&self, _: ShowUnocculted, _a: ApiPieceOpArgs<'_>, _opname: &str, _wrc: WhatResponseToClientOp) - -> Result { + -> Result { throw!(Ia::BadUiOperation) } @@ -244,8 +244,9 @@ pub trait PieceTrait: PieceBaseTrait + Send + Debug + 'static { None } + #[throws(ApiPieceOpError)] fn op_multigrab(&self, _: ApiPieceOpArgs, _: PieceRenderInstructions, - _: MultigrabQty) -> Result { + _: MultigrabQty) -> OpOutcomeThunk { Err(Ia::BadPieceStateForOperation)? } diff --git a/src/hand.rs b/src/hand.rs index 23972717..955e632e 100644 --- a/src/hand.rs +++ b/src/hand.rs @@ -229,7 +229,7 @@ impl PieceTrait for Hand { #[throws(ApiPieceOpError)] fn ui_operation(&self, vis: ShowUnocculted, mut a: ApiPieceOpArgs<'_>, opname: &str, wrc: WhatResponseToClientOp) - -> UpdateFromOpComplex { + -> OpOutcomeThunk { if let Some(r) = { let gpc = a.gs.pieces.byid_mut(a.piece)?; let rot_checked = gpc.occulter_check_unrotated(vis)?; @@ -238,7 +238,7 @@ impl PieceTrait for Hand { internal_error_bydebug(&(&gpc.pos, &self.shape)))?; organise::ui_operation(&mut a, rot_checked, opname, wrc, &rect)? } { - return r; + return r.into(); } let ApiPieceOpArgs { gs,player,piece,ipieces,ioccults,to_recalculate,.. } = a; @@ -342,6 +342,7 @@ impl PieceTrait for Hand { wrc, log, ops: puos.into(), }, xupdates.into_unprepared(None)) + .into() } fn occultation_notify_hook(&self, piece: PieceId) -> UnpreparedUpdates { diff --git a/src/updates.rs b/src/updates.rs index 663ce087..cdbd874e 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -165,6 +165,41 @@ pub enum PieceUpdateOp { SetZLevelQuiet(ZL), } +#[derive(From)] +pub enum OpOutcomeThunk { + Immediate(UpdateFromOpComplex), + /// Allows a UI operation full mutable access to the whole Instance. + /// + /// Use with care! Eg, you might have to call save_game_and_aux_late.r + /// + /// Rules for adding and removing pieces: + /// + /// * Adding a piece: add it to both ipieces and pieces. + /// Call `save_game_and_aux_later`. + /// Aux is always saved first, so if the piece is in pieces, + /// it will be in ipieces on any reload. + /// + /// * Deleting a piece: if the deletion as not the result of + /// some kind of merge, and it doesn't matter if only the + /// deletion happens, and not other recent events: just delete it. + /// Call `save_game_and_aux_later`. + /// + /// * Deleting a piece, in some kind of more complicated situation + /// where the deletion must be atomic with other operations. + /// Delete the piece *only* from pieces. Leave it in ipieces. + /// A reload will always restore a `GameState` snapshot. + /// TODO: we should garbage-collect the slot in ipieces. + /// + /// TODO: Provide `&mut InstanceGuard` to the closure, + /// not `&umut Instance`, since the latter is not sufficient. + /// + /// TODO: Provide cooked methods for this (taking `ModifyingPieces`) + /// + /// TODO: Provide a `ModifyingPieces` to the closure. + Reborrow(Box Result>), +} + pub type UpdateFromOpComplex = ( PieceUpdate, UnpreparedUpdates,