From: Ian Jackson Date: Sun, 26 Jul 2020 15:55:25 +0000 (+0100) Subject: before wrap Instance up X-Git-Tag: otter-0.2.0~1244 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=c4a869cf8356417f24c1851d795ce54452007ea8;p=otter.git before wrap Instance up --- diff --git a/src/api.rs b/src/api.rs index 59245137..8eafb805 100644 --- a/src/api.rs +++ b/src/api.rs @@ -27,7 +27,10 @@ pub trait Lens { fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, player: PlayerId) -> PieceId; } -struct TransparentLens { } +pub struct TransparentLens { + // when lenses become nontrivial, make this nonconstructable + // to find all the places where a TransparentLens was bodged +} impl Lens for TransparentLens { fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId { let kd : slotmap::KeyData = piece.into(); @@ -89,8 +92,8 @@ fn api_piece_op(form : Json>) eprintln!("API {:?} => {:?}", &form, &err); }, Ok((update, logents)) => { - let mut buf = PrepareUpdatesBuffer::new(g, client, form.cseq, - 1 + logents.len()); + let mut buf = PrepareUpdatesBuffer::new(g, Some((client, form.cseq)), + Some(1 + logents.len())); buf.piece_update(piece, update, &lens)?; buf.log_updates(logents)?; diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 3ee23199..94716c9e 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -65,7 +65,8 @@ type ME = MgmtError; #[throws(CSE)] fn decode_and_process(cs: &mut CommandStream, s: &str) { let resp = self::decode_process_inner(cs, s) - .unwrap_or_else(|e| MgmtResponse::Error(format!("{}", e))); + .unwrap_or_else(|e| MgmtResponse::Error( + MgmtError::ParseFailed(format!("{}", e)))); serde_lexpr::to_writer(&mut cs.write, &resp)?; } @@ -196,7 +197,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { Fine { } }, - CreateGame(name) => { + CreateGame(name, subcommands) => { let gs = GameState { pieces : Default::default(), players : Default::default(), @@ -209,11 +210,20 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { scoped_name : name, }; - cs.amu = Some(Instance::new(name, gs)?); + let ami = Instance::new(name, gs)?; + let g = ami.lock(); + + execute_for_game(cs, &ami, subcommands, MgmtGameUpdateMode::Bulk) + .map_err(|e|{ + Instance::destroy(ami); + e + })?; Fine { } }, + + /* AddPiece(game, { pos,count,name,info }) => { let game = cs.lookup_game(&game)?; @@ -227,6 +237,105 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { } } +struct UpdateHandlerBulk { + pieces : SecondarySlotMap>, +} + +enum UpdateHandler { + Bulk(UpdateHandlerBulk), + Online, +} + +impl UpdateHandler { + #[throws(SVGProcessingError)] + fn accumulate(&mut self, g: &mut Instance, + upieces: Vec<(PieceId,PieceUpdateOp<()>)>, + ulogs: Vec) { + use UpdateHandler::*; + match self { + Bulk(bulk) => { + for (upiece, uuop) in upieces { + use PieceUpdateOp::*; + let ne = match (bulk.pieces.get(upiece), uuop) { + ( None , e ) => e, + ( Some( Insert(()) ) , Some( Delete(()) ) ) => None, + ( Some( Insert(()) ) , _ ) => Some( Insert(()) ), + ( Some( Delete(()) ) , _ ) => Some( Modify(()) ), + ( _ , _ ) => Some( Modify(()) ), + }; + match ne { + Some(ne) => bulk.pieces[upiece] = ne, + None => bulk.pieces.remove(upiece), + }; + } + let _logs = ulogs; + }, + Online => { + let estimate = upieces.len() + ulogs.len(); + let buf = PrepareUpdatesBuffer::new(g, None, Some(estimate)); + for (upiece, uuop) in upieces { + let lens = TransparentLens { }; + buf.piece_update(upiece, uuop, &lens)?; + } + buf.log_updates(ulogs)?; + }, + } + } + + #[throws(SVGProcessingError)] + fn complete(&mut self, g: &mut Instance) { + use UpdateHandler::*; + match self { + Bulk(bulk) => { + let buf = PrepareUpdatesBuffer::new(g, None, None); + for (upiece, uuop) in self.pieces { + let lens = TransparentLens { }; + buf.piece_update(upiece, uuop, &lens)?; + } + + buf.log_updates(LogEntry { + html: "The facilitator (re)configured the game".to_owned(), + })?; + }, + Online => { }, + } + } +} + +#[throws(ME)] +fn execute_for_game(cs: &CommandStream, amu: &InstanceRef, + subcommands: Vec, + how: MgmtGameUpdateMode) { + let g = amu.lock()?; + let mut subcommands = subcommands.drain.zip(0..); + let mut uh = UpdateHandler::from_how(how); + let response = 'subcommands: loop { + + let (subcommand, index) = match subcommands.next() { + None => break 'subcommands Fine { }, + Some(r) => r, + }; + + let (upieces, ulogs) = match execute_game_update(&mut g.gs, subcommand) { + Err(e) => break 'subcommands ErrorAfter(index, e), + Ok(r) => r, + }; + + uh.accumulate(upieces, ulogs)?; + + }; + + uh.complete()?; +} + +#[throws(ME)] +fn execute_game_update(gs: &mut GameState, update: MgmtGameUpdate) + -> (Vec<(PieceId,PieceUpdateOp<()>)>, Vec) { + match update { + } +} + + impl CommandListener { #[throws(StartupError)] pub fn new() -> Self { diff --git a/src/commands.rs b/src/commands.rs index 516ca2e3..14391411 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,14 +5,26 @@ use crate::imports::*; pub enum MgmtCommand { Noop { }, SetScope(ManagementScope), - CreateGame(String), + CreateGame(String, Vec), // AddPiece(Box), } +#[derive(Debug,Serialize,Deserialize)] +pub enum MgmtGameUpdate { + Noop { }, +} + #[derive(Debug,Serialize,Deserialize)] pub enum MgmtResponse { Fine { }, - Error(String), + Error(MgmtError), + ErrorAfter(usize, MgmtError), +} + +#[derive(Debug,Serialize,Deserialize)] +pub enum MgmtGameUpdateMode { + Online, + Bulk, } #[derive(Debug,Error)] diff --git a/src/error.rs b/src/error.rs index 6769070a..b0b12b9c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,8 @@ pub enum GameError { #[derive(Error,Debug)] pub enum OnlineError { + #[error("Game in process of being destroyed")] + GameBeingDestroyed, #[error("Game corrupted by previous crash - consult administrator")] GameCorrupted, #[error("client session not recognised (terminated by server?)")] diff --git a/src/global.rs b/src/global.rs index b5a72cf7..9464ea4c 100644 --- a/src/global.rs +++ b/src/global.rs @@ -25,6 +25,7 @@ pub type InstanceRef = Arc>; #[derive(Debug)] pub struct Instance { + pub live : bool, pub name : Arc, pub gs : GameState, pub clients : DenseSlotMap, @@ -122,6 +123,7 @@ impl Instance { let name = Arc::new(name); let inst = Instance { + live : true, name : name.clone(), gs, clients : Default::default(), @@ -147,8 +149,17 @@ impl Instance { #[throws(OE)] pub fn lock<'g>(amu : &'g Arc>) -> InstanceGuard<'g> { let ig = amu.lock()?; + if !ig.live { throw!(OnlineError::GameBeingDestroyed) } InstanceGuard { ig, amu: amu.clone() } } + + pub fn destroy(g: InstanceGuard) { + g.live = false; + // remove the files + GLOBAL.games.write().unwrap().remove(&g.name); + InstanceGuard::forget_all_tokens(&mut g.tokens_players); + InstanceGuard::forget_all_tokens(&mut g.tokens_clients); + } } impl Deref for InstanceGuard<'_> { @@ -184,10 +195,6 @@ impl InstanceGuard<'_> { Id::global_tokens(PRIVATE_Y).write().unwrap().insert(token, iad); } - pub fn game_destroy(mut self) { - Self::forget_all_tokens(&mut self.ig.tokens_players); - Self::forget_all_tokens(&mut self.ig.tokens_clients); - } fn forget_all_tokens(tokens: &mut TokenRegistry) { let global : &RwLock> = AccessId::global_tokens(PRIVATE_Y); let mut global = global.write().unwrap(); @@ -268,6 +275,7 @@ impl InstanceGuard<'_> { let name = Arc::new(name); let inst = Instance { + live: true, name, gs, updates, clients : Default::default(), tokens_clients : Default::default(), diff --git a/src/imports.rs b/src/imports.rs index c0eac3bf..bd47932b 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -64,7 +64,7 @@ pub use crate::error::*; pub use crate::commands::*; pub use crate::slotmap_slot_idx::*; pub use crate::cmdlistener::*; -pub use crate::api::Lens; +pub use crate::api::{Lens,TransparentLens}; pub use libc::uid_t; diff --git a/src/updates.rs b/src/updates.rs index abcffdfa..b7ba42d3 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -161,14 +161,21 @@ pub struct PrepareUpdatesBuffer<'r> { } impl<'r> PrepareUpdatesBuffer<'r> { - pub fn new(g: &'r mut Instance, by_client: ClientId, cseq: ClientSequence, - estimate: usize) -> Self + pub fn new(g: &'r mut Instance, + by_client: Option<(ClientId, ClientSequence)>, + estimate: Option) -> Self { + let by_client = by_client.unwrap_or( + (Default::default(), ClientSequence(0)) + ); + let us = estimate.map(|e| Vec::with_capacity(e)).unwrap_or(vec![]); + g.gs.gen.increment(); + PrepareUpdatesBuffer { - us: Vec::with_capacity(estimate), gen: g.gs.gen, - g, by_client, cseq, + by_client: by_client.0, cseq: by_client.1, + us, g, } }