chiark / gitweb /
before wrap Instance up
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 15:55:25 +0000 (16:55 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 15:55:25 +0000 (16:55 +0100)
src/api.rs
src/cmdlistener.rs
src/commands.rs
src/error.rs
src/global.rs
src/imports.rs
src/updates.rs

index 5924513737563ac9056ddfd406752d9e3f751bfd..8eafb80505b65b97f9cf9883c95bda9523c3ff2a 100644 (file)
@@ -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<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
       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)?;
index 3ee23199eb9171b41df479c090613f6ff18e3f50..94716c9e06ba5f70eb00d2cea921557f918fad42 100644 (file)
@@ -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<PieceId, PieceUpdateOp<()>>,
+}
+
+enum UpdateHandler {
+  Bulk(UpdateHandlerBulk),
+  Online,
+}
+
+impl UpdateHandler {
+  #[throws(SVGProcessingError)]
+  fn accumulate(&mut self, g: &mut Instance,
+                upieces: Vec<(PieceId,PieceUpdateOp<()>)>,
+                ulogs: Vec<LogEntry>) {
+    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<MgmtGameUpdate>,
+                    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<LogEntry>) {
+  match update {
+  }
+}
+
+
 impl CommandListener {
   #[throws(StartupError)]
   pub fn new() -> Self {
index 516ca2e3b862ec5131e0dbf77f9129b87b19c366..1439141119157b3677edc4c3e11098d61322863f 100644 (file)
@@ -5,14 +5,26 @@ use crate::imports::*;
 pub enum MgmtCommand {
   Noop { },
   SetScope(ManagementScope),
-  CreateGame(String),
+  CreateGame(String, Vec<MgmtGameUpdate>),
 //  AddPiece(Box<dyn PieceSpec>),
 }
 
+#[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)]
index 6769070a4633a69fd75b462f746ba320b9e2e576..b0b12b9cbab0187f27703ba1958a437c51799a31 100644 (file)
@@ -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?)")]
index b5a72cf7ee59102590efab1736c95adb881110d8..9464ea4c2eeeadbb661d4493e583313470f20945 100644 (file)
@@ -25,6 +25,7 @@ pub type InstanceRef = Arc<Mutex<Instance>>;
 
 #[derive(Debug)]
 pub struct Instance {
+  pub live : bool,
   pub name : Arc<InstanceName>,
   pub gs : GameState,
   pub clients : DenseSlotMap<ClientId,Client>,
@@ -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<Mutex<Instance>>) -> 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<Id:AccessId>(tokens: &mut TokenRegistry<Id>) {
     let global : &RwLock<TokenTable<Id>> = 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(),
index c0eac3bf1fbdc8b06d95de0689de3927dcfbbc2b..bd47932b04aaf7d1e7e35b8bb05d88354b3ad070 100644 (file)
@@ -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;
 
index abcffdfa8d2a635812f65ef90efc4cf2df54fe9c..b7ba42d3887af941bbe1fbb7b4f6534069baf040 100644 (file)
@@ -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<usize>) -> 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,
     }
   }