chiark / gitweb /
updates: New "PreparedUpdateEntry_Image"
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 20 Mar 2021 14:03:19 +0000 (14:03 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 20 Mar 2021 19:29:16 +0000 (19:29 +0000)
This allows server code to just update the SVG without causing cseq
updates, conflicts, etc.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/updates.rs
templates/script.ts

index 25be6382b9a78f4411e1c7f88a5d1346dc437b30..265ae6a5a4c24e61b1d837292a9be189f4c94765 100644 (file)
@@ -8,6 +8,8 @@ use crate::prelude::*;
 
 #[allow(non_camel_case_types)] type PUE_P = PreparedUpdateEntry_Piece;
 #[allow(non_camel_case_types)] type TUE_P<'u> = TransmitUpdateEntry_Piece<'u>;
+#[allow(non_camel_case_types)] type PUE_I = PreparedUpdateEntry_Image;
+#[allow(non_camel_case_types)] type TUE_I<'u> = TransmitUpdateEntry_Image<'u>;
 
 // ---------- newtypes, type aliases, basic definitions ----------
 
@@ -52,6 +54,7 @@ pub struct PreparedUpdate {
 #[derive(Debug)]
 pub enum PreparedUpdateEntry {
   Piece(PreparedUpdateEntry_Piece),
+  Image(PreparedUpdateEntry_Image),
   SetTableSize(Pos),
   SetTableColour(Colour),
   SetLinks(Arc<LinksTable>),
@@ -75,10 +78,22 @@ pub struct PreparedUpdateEntry_Piece {
   ops: SecondarySlotMap<PlayerId, PreparedPieceUpdate>,
 }
 
+pub type PreparedPieceUpdate = PreparedPieceUpdateGeneral<
+    PieceUpdateOp<PreparedPieceState, ZLevel>
+    >;
+
+#[allow(non_camel_case_types)]
 #[derive(Debug,Clone)]
-pub struct PreparedPieceUpdate {
+pub struct PreparedUpdateEntry_Image {
+  ims: SecondarySlotMap<PlayerId, PreparedPieceUpdateGeneral<
+      PreparedPieceImage
+      >>,
+}
+
+#[derive(Debug,Clone)]
+pub struct PreparedPieceUpdateGeneral<U> {
   piece: VisiblePieceId,
-  op: PieceUpdateOp<PreparedPieceState, ZLevel>,
+  op: U,
 }
 
 #[derive(Debug,Clone,Serialize)]
@@ -93,11 +108,36 @@ pub struct PreparedPieceState {
   pub uos: Vec<UoDescription>,
 }
 
+#[derive(Debug,Clone,Serialize)]
+pub struct PreparedPieceImage {
+  pub svg: Html,
+  pub uos: Vec<UoDescription>,
+}
+
 #[derive(Serialize,Debug)]
 pub struct DataLoadPlayer {
   dasharray: Html,
 }
 
+pub trait JsonLen {
+  fn json_len(&self) -> usize;
+}
+
+#[ext]
+impl<T:JsonLen> SecondarySlotMap<PlayerId, T> {
+  fn json_len(&self, player: PlayerId) -> usize {
+    if let Some(t) = self.get(player) {
+      50 + t.json_len()
+    } else {
+      50
+    }
+  }
+}
+
+impl JsonLen for Html {
+  fn json_len(&self) -> usize { self.0.as_bytes().len() }
+}
+
 // ---------- piece updates ----------
 
 #[derive(Debug,Clone,Copy,Serialize)]
@@ -163,6 +203,7 @@ enum TransmitUpdateEntry<'u> {
     svg: Option<&'u Html>, // IsResponseToClientOp::UpdateSvg
   },
   Piece(TransmitUpdateEntry_Piece<'u>),
+  Image(TransmitUpdateEntry_Image<'u>),
   RecordedUnpredictable {
     piece: VisiblePieceId,
     cseq: ClientSequence,
@@ -194,6 +235,13 @@ struct TransmitUpdateEntry_Piece<'u> {
   op: PieceUpdateOp<&'u PreparedPieceState, &'u ZLevel>,
 }
 
+#[allow(non_camel_case_types)]
+#[derive(Debug,Serialize)]
+struct TransmitUpdateEntry_Image<'u> {
+  piece: VisiblePieceId,
+  im: &'u PreparedPieceImage,
+}
+
 #[derive(Debug,Serialize)]
 struct FormattedLogEntry<'u> {
   when: String,
@@ -295,16 +343,40 @@ impl PreparedUpdate {
 
 impl PreparedUpdateEntry_Piece {
   pub fn json_len(&self, player: PlayerId) -> usize {
-    let PUE_P { ref ops, .. } = self;
-    if let Some(op) = ops.get(player) {
-      50 +
-        op.op.new_state().map(|x| x.svg.0.as_bytes().len()).unwrap_or(0)
-    } else {
-      50
-    }
+    50 + self.ops.json_len(player)
+  }
+}
+
+impl<U:JsonLen> JsonLen for PreparedPieceUpdateGeneral<U> {
+  fn json_len(&self) -> usize { self.op.json_len() }
+}
+
+impl<NS:JsonLen, ZL> JsonLen for PieceUpdateOp<NS, ZL> {
+  fn json_len(&self) -> usize {
+    self.new_state().map(|x| x.json_len()).unwrap_or(0)
+  }
+}
+
+impl PreparedUpdateEntry_Image {
+  fn json_len(&self, player: PlayerId) -> usize {
+    self.ims.json_len(player)
   }
 }
 
+impl JsonLen for PreparedPieceState {
+  fn json_len(&self) -> usize { self.svg.json_len() + self.uos.json_len() }
+}
+
+impl JsonLen for PreparedPieceImage {
+  fn json_len(&self) -> usize { self.svg.json_len() + self.uos.json_len() }
+}
+impl<T:JsonLen> JsonLen for Vec<T> {
+  fn json_len(&self) -> usize { self.iter().map(|x| x.json_len() + 10).sum() }
+}
+impl JsonLen for UoDescription {
+  fn json_len(&self) -> usize { self.desc.json_len() + 50 }
+}
+
 impl PreparedUpdateEntry {
   pub fn json_len(&self, player: PlayerId) -> usize {
     use PreparedUpdateEntry::*;
@@ -312,6 +384,9 @@ impl PreparedUpdateEntry {
       Piece(op) => {
         op.json_len(player)
       }
+      Image(ims) => {
+        ims.json_len(player)
+      }
       Log(logent) => {
         logent.logent.html.0.as_bytes().len() * 28
       }
@@ -679,6 +754,12 @@ impl PreparedUpdate {
       Some(TUE_P { piece, op: op.map_ref() })
     }
 
+    fn pue_image_to_tue_i(pue_i: &PUE_I, player: PlayerId) -> Option<TUE_I> {
+      let im = pue_i.ims.get(player)?;
+      let PreparedPieceUpdateGeneral { piece, ref op } = *im;
+      Some(TUE_I { piece, im: op })
+    }
+
     fn pue_piece_to_tue(pue_p: &PUE_P, player: PlayerId, dest: ClientId)
                         -> Option<TUE> {
       let PUE_P { by_client, ref ops } = *pue_p;
@@ -723,6 +804,12 @@ impl PreparedUpdate {
             _ => continue,
           }
         }
+        &PUE::Image(ref pue_i) => {
+          match pue_image_to_tue_i(pue_i, player) {
+            Some(tue) => TUE::Image(tue),
+            _ => continue,
+          }
+        }
         PUE::Log(logent) => {
           TUE::Log((&tz, &logent))
         }
index f167d67e0d18e43232221b121e12d5de5e97a938..f971fe63b47a8ad39b55927e13d9d6b7b9b43a5b 100644 (file)
@@ -1055,16 +1055,21 @@ piece_error_handlers.PosOffTable = <PieceErrorHandler>function()
 piece_error_handlers.Conflict = <PieceErrorHandler>function()
 { return true ; }
 
-function piece_modify(piece: PieceId, p: PieceInfo, info: PreparedPieceState,
-                     conflict_expected: boolean) {
+function piece_modify_image(piece: PieceId, p: PieceInfo,
+                           info: PreparedPieceImage) {
   p.delem.innerHTML = info.svg;
   p.pelem= piece_element('piece',piece)!;
+  p.uos = info.uos;
+}
+
+function piece_modify(piece: PieceId, p: PieceInfo, info: PreparedPieceState,
+                     conflict_expected: boolean) {
+  piece_modify_image(piece, p, info);
   p.uelem.setAttributeNS(null, "x", info.pos[0]+"");
   p.uelem.setAttributeNS(null, "y", info.pos[1]+"");
   p.held = info.held;
   p.pinned = info.pinned;
   p.angle = info.angle;
-  p.uos = info.uos;
   piece_set_zlevel(piece,p, (oldtop_piece)=>{
     p.z  = info.z;
     p.zg = info.zg;
@@ -1075,6 +1080,26 @@ function piece_modify(piece: PieceId, p: PieceInfo, info: PreparedPieceState,
   console.log('MODIFY DONE');
 }
 
+type PreparedPieceImage = {
+  svg: string,
+  uos: UoDescription[],
+}
+
+type TransmitUpdateEntry_Image = {
+  piece: PieceId,
+  im: PreparedPieceImage,
+};
+
+messages.Image = <MessageHandler>function(j: TransmitUpdateEntry_Image) {
+  console.log('IMAGE UPDATE ',j)
+  var piece = j.piece;
+  let p = pieces[piece]!;
+  piece_modify_image(piece, p, j.im);
+  redisplay_ancillaries(piece,p);
+  recompute_keybindings();
+  console.log('IMAGE DONE');
+}
+
 /*
 pieceops.Insert = <PieceHandler>function
 (piece: PieceId, p: null,