chiark / gitweb /
Rename SetPlayer to AddPlayer
[otter.git] / src / updates.rs
1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
4
5 // update messages from server to client
6
7 use crate::prelude::*;
8
9 use std::ops::Neg;
10
11 #[path="movehist.rs"] pub mod movehist;
12
13 #[allow(non_camel_case_types)] type TUE_P<'u> = TransmitUpdateEntry_Piece<'u>;
14 #[allow(non_camel_case_types)] type PUE_I = PreparedUpdateEntry_Image;
15 #[allow(non_camel_case_types)] type TUE_I<'u> = TransmitUpdateEntry_Image<'u>;
16
17 // ---------- newtypes, type aliases, basic definitions ----------
18
19 #[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
20 #[derive(Serialize,Deserialize)]
21 #[serde(transparent)]
22 pub struct UpdateId(i64);
23
24 pub type RawClientSequence = u64;
25
26 #[derive(Debug,Copy,Clone,Eq,PartialEq,Deserialize,Serialize)]
27 #[serde(transparent)]
28 pub struct ClientSequence(RawClientSequence);
29
30 pub type UnpreparedUpdates = Vec<SomeUnpreparedUpdates>;
31 pub type SomeUnpreparedUpdates = Box<
32     dyn for<'r> FnOnce(&'r mut PrepareUpdatesBuffer)
33     >;
34
35 // ---------- from manamgenet operations ----------
36
37 #[derive(Debug)] // not Default
38 pub struct ExecuteGameChangeUpdates {
39   pub pcs: Vec<(PieceId, PieceUpdateOp<(), ()>)>,
40   pub log: Vec<LogEntry>,
41   pub raw: Option<Vec<PreparedUpdateEntry>>,
42 }
43
44 // ---------- prepared updates, queued in memory ----------
45
46 pub type PlayerUpdatesLog =
47   StableIndexVecDeque<Arc<PreparedUpdate>,UpdateId>;
48
49 #[derive(Debug)]
50 pub struct PlayerUpdates {
51   log: PlayerUpdatesLog,
52   cv: Arc<Condvar>,
53 }
54
55 #[derive(Debug)]
56 pub struct PreparedUpdate {
57   pub gen: Generation,
58   pub when: Instant,
59   pub us: Vec<PreparedUpdateEntry>,
60 }
61
62 #[derive(Debug)]
63 pub enum PreparedUpdateEntry {
64   Piece(PreparedUpdateEntry_Piece),
65   Image(PreparedUpdateEntry_Image),
66   MoveHistEnt(SecondarySlotMap<PlayerId, movehist::Ent>),
67   MoveHistClear,
68   SetTableSize(Pos),
69   SetTableColour(Colour),
70   SetLinks(Arc<LinksTable>),
71   SetPlayer {
72     player: PlayerId,
73     data: DataLoadPlayer,
74     new_info_pane: Arc<Html>,
75   },
76   RemovePlayer {
77     player: PlayerId,
78     new_info_pane: Arc<Html>,
79   },
80   UpdateBundles {
81     new_info_pane: Arc<Html>,
82   },
83   Log(Arc<CommittedLogEntry>),
84   Error(ErrorSignaledViaUpdate<PUE_P, String>),
85 }
86
87 #[allow(non_camel_case_types)]
88 #[derive(Debug,Clone)]
89 pub struct PreparedUpdateEntry_Piece {
90   by_client: IsResponseToClientOp,
91   ops: SecondarySlotMap<PlayerId, PreparedPieceUpdate>,
92 }
93
94 pub type PreparedPieceUpdate = PreparedPieceUpdateGeneral<
95     PieceUpdateOp<PreparedPieceState, ZLevel>
96     >;
97
98 #[allow(non_camel_case_types)]
99 #[derive(Debug,Clone)]
100 pub struct PreparedUpdateEntry_Image {
101   ims: SecondarySlotMap<PlayerId, PreparedPieceUpdateGeneral<
102       PreparedPieceImage
103       >>,
104 }
105
106 #[derive(Debug,Clone)]
107 pub struct PreparedPieceUpdateGeneral<U> {
108   piece: VisiblePieceId,
109   op: U,
110 }
111
112 #[derive(Debug,Clone,Serialize)]
113 pub struct PreparedPieceState {
114   pub pos: Pos,
115   pub svg: Html,
116   pub desc: Html,
117   pub facehint: Option<FaceId>,
118   pub held: Option<PlayerId>,
119   pub z: ZCoord,
120   pub zg: Generation,
121   pub angle: CompassAngle,
122   pub pinned: bool,
123   pub moveable: PieceMoveable,
124   pub rotateable: bool,
125   pub multigrab: bool,
126   pub uos: Vec<UoDescription>,
127   pub occregion: Option<JsonString<Region>>,
128   pub bbox: Rect,
129 }
130
131 #[derive(Debug,Copy,Clone,Serialize,Deserialize,Eq)]
132 #[derive(Ord,PartialEq,PartialOrd)]
133 pub enum PieceMoveable {
134   No,
135   IfWresting,
136   Yes,
137 }
138 impl Default for PieceMoveable { fn default() -> Self { PieceMoveable::Yes } }
139
140 #[derive(Debug,Clone,Serialize)]
141 pub struct PreparedPieceImage {
142   pub svg: Html,
143   pub desc: Html,
144   pub bbox: Rect,
145   pub uos: Vec<UoDescription>,
146 }
147
148 #[derive(Serialize,Debug)]
149 pub struct DataLoadPlayer {
150   dasharray: Html,
151   nick: Html,
152 }
153
154 // ---------- piece updates ----------
155
156 #[derive(Debug,Clone,Copy,Serialize)]
157 // Quiet means not to produce the yellow halo (see `movements` in script.ts)
158 pub enum PieceUpdateOp<NS,ZL> {
159   Delete        (),
160   Insert        (NS),
161   InsertQuiet   (NS),
162   Modify        (NS),
163   ModifyQuiet   (NS),
164   Move          (Pos),
165   MoveQuiet     (Pos),
166   SetZLevel     (ZL),
167   SetZLevelQuiet(ZL),
168 }
169
170 #[derive(From,Educe)]
171 #[educe(Default(bound="T: Default"))]
172 pub enum OpOutcomeThunkGeneric<A,T,E> {
173   #[educe(Default)]
174   Immediate(T),
175   /// Allows an operation full mutable access to the whole Instance.
176   ///
177   /// Use with care!  Eg, you might have to call save_game_and_aux_later.
178   ///
179   /// Adding and removing pieces during play (rather than management)
180   /// is complicated, because we want to avoid having to rewrite the aux.
181   /// file during routine game saves.  `fastsplit.rs` has machinery that
182   /// can achieve this.
183   Reborrow(Box<dyn FnOnce(&mut InstanceGuard, A) -> Result<T,E>>),
184 }
185
186 pub type OpOutcomeThunk = OpOutcomeThunkGeneric<
187     (PlayerId, PieceId), UpdateFromOpComplex, ApiPieceOpError>;
188
189 pub type OpHookThunk = OpOutcomeThunkGeneric<
190     (PlayerId,), UnpreparedUpdates, InternalError>;
191
192 pub type UpdateFromOpComplex = (
193   PieceUpdate,
194   UnpreparedUpdates,
195 );
196
197 pub type PieceUpdateFromOpSimple = (
198   WhatResponseToClientOp,
199   PieceUpdateOp<(),()>,
200   Vec<LogEntry>,
201 );
202 pub type PieceUpdateResult = Result<PieceUpdate, ApiPieceOpError>;
203
204 #[derive(Debug)]
205 pub struct PieceUpdate {
206   pub wrc: WhatResponseToClientOp,
207   pub log: Vec<LogEntry>,
208   pub ops: PieceUpdateOps,
209 }
210
211 #[derive(Debug)]
212 pub enum PieceUpdateOps {
213   Simple(PieceUpdateOp<(),()>),
214   PerPlayer(PieceUpdateOps_PerPlayer),
215 }
216
217 #[allow(non_camel_case_types)]
218 pub type PieceUpdateOps_PerPlayer =
219   SecondarySlotMap<PlayerId, PieceUpdateOp<(),()>>;
220
221 impl From<PieceUpdateOp<(),()>> for PieceUpdateOps {
222   fn from(op: PieceUpdateOp<(),()>) -> Self { PUOs::Simple(op) }
223 }
224
225 impl From<PieceUpdateFromOpSimple> for PieceUpdate {
226   fn from((wrc, op, log): PieceUpdateFromOpSimple) -> Self {
227     PieceUpdate { wrc, log, ops: op.into() }
228   }
229 }
230
231
232 #[allow(non_camel_case_types)]
233 #[derive(Copy,Clone,Debug)]
234 pub struct PUOs_Simple_Modify;
235 impl From<PUOs_Simple_Modify> for PieceUpdateOps {
236   fn from(_: PUOs_Simple_Modify) -> Self { PUOs::Simple(PUO::Modify(())) }
237 }
238
239 // ---------- for traansmission ----------
240
241 #[derive(Debug,Serialize)]
242 pub struct TransmitUpdate<'u> (
243   Generation,
244   Vec<TransmitUpdateEntry<'u>>,
245 );
246
247 #[derive(Debug,Serialize)]
248 enum TransmitUpdateEntry<'u> {
249   Recorded {
250     piece: VisiblePieceId,
251     cseq: ClientSequence,
252     zg: Option<Generation>,
253     svg: Option<&'u Html>, // IsResponseToClientOp::UpdateSvg
254     desc: Option<&'u Html>,
255   },
256   Piece(TransmitUpdateEntry_Piece<'u>),
257   Image(TransmitUpdateEntry_Image<'u>),
258   MoveHistEnt(&'u movehist::Ent),
259   MoveHistClear{},
260   RecordedUnpredictable {
261     piece: VisiblePieceId,
262     cseq: ClientSequence,
263     ns: &'u PreparedPieceState,
264   },
265   SetTableSize(Pos),
266   SetTableColour(&'u Colour),
267   SetPlayer {
268     player: PlayerId,
269     data: &'u DataLoadPlayer,
270     new_info_pane: &'u Html,
271   },
272   RemovePlayer {
273     player: PlayerId,
274     new_info_pane: &'u Html,
275   },
276   UpdateBundles {
277     new_info_pane: &'u Arc<Html>,
278   },
279   SetLinks(Html),
280   #[serde(serialize_with="serialize_logentry")]
281   Log(TransmitUpdateLogEntry<'u>),
282   Error(ErrorSignaledViaUpdate<ETUE_P<'u>, &'u str>),
283 }
284
285 type TransmitUpdateLogEntry<'u> = (&'u Timezone, &'u CommittedLogEntry);
286
287 #[allow(non_camel_case_types)]
288 #[derive(Debug,Serialize)]
289 struct ErrorTransmitUpdateEntry_Piece<'u> {
290   cseq: Option<ClientSequence>,
291   #[serde(flatten)]
292   tue: TransmitUpdateEntry_Piece<'u>,
293 }
294 #[allow(non_camel_case_types)]
295 type ETUE_P<'u> = ErrorTransmitUpdateEntry_Piece<'u>;
296
297 #[allow(non_camel_case_types)]
298 #[derive(Debug,Serialize)]
299 struct TransmitUpdateEntry_Piece<'u> {
300   piece: VisiblePieceId,
301   op: PieceUpdateOp<&'u PreparedPieceState, &'u ZLevel>,
302 }
303
304 #[allow(non_camel_case_types)]
305 #[derive(Debug,Serialize)]
306 struct TransmitUpdateEntry_Image<'u> {
307   piece: VisiblePieceId,
308   im: &'u PreparedPieceImage,
309 }
310
311 #[derive(Debug,Serialize)]
312 struct FormattedLogEntry<'u> {
313   when: String,
314   logent: &'u LogEntry,
315 }
316
317 // ========== implementation ==========
318
319 // ---------- helpful utilities ----------
320
321 #[throws(Fatal)]
322 pub fn log_did_to_piece_whoby(ioccults: &IOccults, goccults: &GOccults,
323                               by_gpl: &GPlayer,
324                               gpc: &GPiece, ipc: &IPiece, did: &str)
325                               -> (Vec<LogEntry>, Option<Html>)
326 {
327   let who_by = by_gpl.nick.to_html();
328   let y = gpc.fully_visible_to_everyone();
329   let desc = (||{
330     Ok::<_,IE>(match ipc.show_or_instead(ioccults, y)? {
331       Left(y) => ipc.show(y).describe_html(gpc, goccults)?,
332       Right(instead) => instead.describe_html(default())?,
333     })
334   })().unwrap_or_else(|e|{
335     error!("failed to format during logging: {:?}", e);
336     Html::lit("&lt;internal error&gt;").into()
337   });
338
339   let log = vec![ LogEntry { html: hformat!(
340     "{} {} {}",
341     who_by,
342     did,
343     desc,
344   )}];
345   (log, Some(who_by))
346 }
347
348 #[throws(Fatal)]
349 pub fn log_did_to_piece(ioccults: &IOccults, goccults: &GOccults,
350                         by_gpl: &GPlayer,
351                         gpc: &GPiece, ipc: &IPiece, did: &str)
352                         -> Vec<LogEntry> {
353   log_did_to_piece_whoby(ioccults,goccults,by_gpl,gpc,ipc,did)?.0
354 }
355
356 // ---------- support implementation ----------
357
358 impl Neg for UpdateId {
359   type Output = Self;
360   fn neg(self) -> Self { UpdateId(-self.0) }
361 }
362
363 impl Display for UpdateId {
364   fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
365     Display::fmt(&self.0, f)
366   }
367 }
368
369 impl Bounded for UpdateId {
370   fn max_value() -> Self { UpdateId(Bounded::max_value()) }
371   fn min_value() -> Self { UpdateId(Bounded::min_value()) }
372 }
373
374 impl StableIndexOffset for UpdateId {
375   fn try_increment(&mut self) -> Option<()> { self.0.try_increment() }
376   fn try_decrement(&mut self) -> Option<()> { self.0.try_decrement() }
377   fn index_input(&self, input: Self) -> Option<usize> {
378     self.0.index_input(input.0)
379   }
380   fn index_output(&self, inner: usize) -> Option<Self> {
381     self.0.index_output(inner).map(UpdateId)
382   }
383   fn zero() -> Self { UpdateId(0) }
384 }
385
386 impl<A,T,E> OpOutcomeThunkGeneric<A,T,E> {
387   pub fn resolve(self, ig: &mut InstanceGuard, args: A) -> Result<T,E> {
388     match self {
389       OOTG::Immediate(uu) => Ok(uu),
390       OOTG::Reborrow(f) => f(ig, args),
391     }
392   }
393 }
394
395 // ---------- prepared updates, queued in memory ----------
396
397 pub struct PlayerUpdatesStartContext {
398   pub(self) u1: Arc<PreparedUpdate>,
399 }
400
401 impl PlayerUpdatesStartContext {
402   pub fn for_player(&self) -> PlayerUpdates {
403     let mut log = StableIndexVecDeque::with_capacity(50);
404     log.push_back(self.u1.clone());
405     let cv = default();
406     PlayerUpdates { log, cv }
407   }
408 }
409
410 impl PlayerUpdates {
411   pub fn start(gs: &GameState) -> PlayerUpdatesStartContext {
412     let u1 = Arc::new(PreparedUpdate {
413       gen: gs.gen,
414       when: Instant::now(),
415       us: vec![],
416     });
417     PlayerUpdatesStartContext { u1 }
418   }
419
420   pub fn push<U: Into<Arc<PreparedUpdate>>>(&mut self, update: U) {
421     self.log.push_back(update.into());
422     self.cv.notify_all();
423   }
424   // giving out only immutable references means no-one can
425   // forget to cv.notify
426   pub fn read_log(&self) -> &PlayerUpdatesLog { &self.log }
427   pub fn get_cv(&self) -> Arc<Condvar> { self.cv.clone() }
428
429   pub fn expire_upto(&mut self, before: Instant) {
430     loop {
431       if self.log.len() < 2 { break }
432
433       let front = {
434         if let Some(front) = self.log.front() { front }
435         else { break }
436       };
437       if front.when >= before { break }
438
439       trace!("update expiring #{}", self.log.front_index());
440       self.log.pop_front();
441     }
442   }
443 }
444
445 impl DataLoadPlayer {
446   pub fn from_player(ig: &Instance, player: PlayerId) -> Self {
447     let gplayers = &ig.gs.players;
448     let dasharray = player_dasharray(gplayers, player);
449     let nick = gplayers.get(player).map(|gpl| gpl.nick.as_str())
450       .unwrap_or("<unknown-player>")
451       .to_html();
452     DataLoadPlayer {
453       dasharray,
454       nick,
455     }
456   }
457 }
458
459 // ---------- PieceUpdatesOp ----------
460
461 impl<NS,ZC> PieceUpdateOp<NS,ZC> {
462   pub fn new_state(&self) -> Option<&NS> {
463     use PieceUpdateOp::*;
464     match self {
465       Delete        ()   => None,
466       Insert        (ns) => Some(ns),
467       InsertQuiet   (ns) => Some(ns),
468       Modify        (ns) => Some(ns),
469       ModifyQuiet   (ns) => Some(ns),
470       Move          (_)  => None,
471       MoveQuiet     (_)  => None,
472       SetZLevel     (_)  => None,
473       SetZLevelQuiet(_)  => None,
474     }
475   }
476   pub fn try_map<NS2, ZC2, E:Error,
477                  F: FnOnce(NS) -> Result<NS2,E>,
478                  G: FnOnce(ZC) -> Result<ZC2,E>
479                  > (self, f:F, g:G) -> Result<PieceUpdateOp<NS2,ZC2>,E>
480   {
481     use PieceUpdateOp::*;
482     Ok(match self {
483       Delete        ()    => Delete        (),
484       Insert        (ns)  => Insert        (f(ns)?),
485       InsertQuiet   (ns)  => InsertQuiet   (f(ns)?),
486       Modify        (ns)  => Modify        (f(ns)?),
487       ModifyQuiet   (ns)  => ModifyQuiet   (f(ns)?),
488       Move          (pos) => Move          (pos),
489       MoveQuiet     (pos) => MoveQuiet     (pos),
490       SetZLevel     (zl)  => SetZLevel     (g(zl)?),
491       SetZLevelQuiet(zl)  => SetZLevelQuiet(g(zl)?),
492     })
493   }
494   pub fn map_ref(&self) -> PieceUpdateOp<&NS,&ZC> {
495     use PieceUpdateOp::*;
496     match self {
497       Delete        ()    => Delete        (),
498       Insert        (ns)  => Insert        (ns),
499       InsertQuiet   (ns)  => InsertQuiet   (ns),
500       Modify        (ns)  => Modify        (ns),
501       ModifyQuiet   (ns)  => ModifyQuiet   (ns),
502       Move          (pos) => Move          (*pos),
503       MoveQuiet     (pos) => MoveQuiet     (*pos),
504       SetZLevel     (zl)  => SetZLevel     (zl),
505       SetZLevelQuiet(zl)  => SetZLevelQuiet(zl),
506     }
507   }
508   pub fn map<NS2,ZC2,
509              F: FnOnce(NS) -> NS2,
510              G: FnOnce(ZC) -> ZC2
511              > (self, f:F, g:G) -> PieceUpdateOp<NS2,ZC2>
512   {
513     self.try_map(
514       |ns| <Result<_,Void>>::Ok(f(ns)),
515       |zc| <Result<_,Void>>::Ok(g(zc)),
516     ).void_unwrap()
517   }
518   pub fn new_z_generation(&self) -> Option<Generation>
519     where ZC: Borrow<ZLevel>
520   {
521     use PieceUpdateOp::*;
522     match self {
523       Delete        ()  => None,
524       Insert        (_) => None,
525       InsertQuiet   (_) => None,
526       Modify        (_) => None,
527       ModifyQuiet   (_) => None,
528       Move          (_) => None,
529       MoveQuiet     (_) => None,
530       SetZLevel     (l) => Some(l.borrow().zg),
531       SetZLevelQuiet(l) => Some(l.borrow().zg),
532     }
533   }
534 }
535
536 pub struct PrepareUpdatesBuffer<'r> {
537   g: &'r mut Instance,
538   us: Vec<PreparedUpdateEntry>,
539   gen: Option<Generation>,
540 }
541
542 /// In PROTOCOL.md terms, None is a Server update
543 type IsResponseToClientOp = Option<(
544   WhatResponseToClientOp,
545   ClientId,
546   ClientSequence
547 )>;
548
549 #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
550 pub enum WhatResponseToClientOp {
551   /// In PROTOCOL.md terms, a Client update
552   Predictable,
553   /// In PROTOCOL.md terms, a Client update which also updates
554   /// the visible piece image (which is just server-controlled).
555   UpdateSvg,
556   /// In PROTOCOL.md terms, a Client update which results in
557   /// an immediate Server update.  When the client knows this
558   /// is going to happen it can help the user avoid conflicts.
559   Unpredictable,
560 }
561
562 impl<'r> PrepareUpdatesBuffer<'r> {
563   pub fn new(g: &'r mut Instance, estimate: Option<usize>) -> Self
564   {
565     let us = estimate.map_or(vec![], Vec::with_capacity);
566
567     PrepareUpdatesBuffer {
568       gen: None,
569       us, g,
570     }
571   }
572
573   #[throws(IE)]
574   pub fn spontaneous_image(g: &'r mut Instance,
575                            piece: PieceId,
576                            estimate: Option<usize>)
577   {
578     let mut updates = PrepareUpdatesBuffer::new(g, estimate);
579     updates.piece_update_image(piece, &None)?;
580     updates.finish();
581   }
582
583   pub fn gen(&mut self) -> Generation {
584     let gs = &mut self.g.gs;
585     *self.gen.get_or_insert_with(||{
586       gs.gen.increment();
587       gs.gen
588     })
589   }
590
591   pub fn piece_report_error(ig: &mut Instance,
592                             error: Inapplicable, piece: PieceId,
593                             logents: Vec<LogEntry>,
594                             partially: PieceOpErrorPartiallyProcessed,
595                             client: ClientId, cseq: ClientSequence)
596                             -> Result<(),Fatal> {
597     let by_client = (WRC::Unpredictable, client, cseq);
598     let mut buf = PrepareUpdatesBuffer::new(ig, None);
599     let ops = PUOs::Simple(PieceUpdateOp::Modify(()));
600     let state = buf.piece_update_fallible(
601       piece, &Some(by_client), ops, |pc, gen, _by_client| {
602         match partially {
603           POEPP::Unprocessed => { }
604           POEPP::Partially => { pc.gen = gen; pc.lastclient = default(); }
605         }
606       }
607     )?;
608     let update = PUE::Error(ESVU::PieceOpError {
609       error, state, partially,
610       error_msg: error.to_string(),
611     });
612     buf.us.push(update);
613     buf.log_updates(logents);
614     Ok(())
615   }
616
617   #[throws(InternalError)]
618   fn piece_update_player(ioccults: &IOccults,
619                          gs: &GameState,
620                          gpc: &GPiece,
621                          ipc: &IPiece,
622                          op: PieceUpdateOp<(),()>,
623                          pri: &Option<PieceRenderInstructions>)
624                          -> Option<PreparedPieceUpdate>
625   {
626     let pri = match pri { Some(pri) => pri, None => return None };
627
628
629     let op = pri.map_piece_update_op(ioccults, gs, gpc, ipc, op)?;
630     op.map(|op| PreparedPieceUpdate {
631       piece: pri.vpid,
632       op,
633     })
634   }
635
636
637   #[throws(InternalError)]
638   fn piece_update_fallible_players<U,GUF,WMZ,MUF,MPF>
639     (&mut self, piece: PieceId, by_client: &IsResponseToClientOp,
640      gen_update: GUF,
641      mut with_max_z: WMZ,
642      mut mk_update: MUF,
643      mut missing: MPF,
644     )
645      -> SecondarySlotMap<PlayerId, U>
646   where
647     GUF: FnOnce(&mut GPiece, Generation, &IsResponseToClientOp),
648     WMZ: FnMut(&mut ZLevel, &GPiece),
649     MUF: FnMut(&IOccults, &GameState, &GPiece, &IPiece,
650                PlayerId, &Option<PieceRenderInstructions>)
651                -> Result<Option<U>,IE>,
652     MPF: FnMut(&mut GPlayer) -> Option<U>,
653   {
654     let gen = self.gen();
655     let gs = &mut self.g.gs;
656     let ioccults = &self.g.ioccults;
657
658     let mut gpc = gs.pieces.byid_mut(piece).ok();
659     let ipc = self.g.ipieces.get(piece);
660
661     if let Some(ref mut gpc) = gpc {
662       gen_update(gpc, gen, by_client);
663     }
664     let gpc = gs.pieces.byid(piece).ok();
665
666     let mut out: SecondarySlotMap<PlayerId, U> = default();
667     for player in self.g.iplayers.keys() {
668       // Iterate via iplayers because we want to borrow each gpl
669       // mutably and also gs immutably, at different times.  The naive
670       // approach fails because the iterator must borrow the whole
671       // thing, and mutably to produce mut references, so ties everything
672       // in a knot.
673       let gpl = match gs.players.get_mut(player) { Some(v)=>v, _=>continue };
674       let upd = match (gpc, ipc) {
675         (Some(gpc), Some(ipc)) => {
676           let pri = piece_pri(ioccults, &gs.occults, player,
677                               gpl, piece, gpc, ipc);
678           with_max_z(&mut gs.max_z, gpc);
679           mk_update(ioccults,gs,gpc,ipc,player,&pri)?
680         },
681         _ => {
682           missing(gpl)
683         },
684       };
685
686       if let Some(upd) = upd {
687         out.insert(player, upd);
688       }
689     }
690     out
691   }
692
693   #[throws(InternalError)]
694   fn piece_update_fallible<GUF>(&mut self, piece: PieceId,
695                                 by_client: &IsResponseToClientOp,
696                                 ops: PieceUpdateOps,
697                                 gen_update: GUF)
698                                 -> PreparedUpdateEntry_Piece
699     where GUF: FnOnce(&mut GPiece, Generation, &IsResponseToClientOp)
700   {
701     let ops = self.piece_update_fallible_players
702       ::<PreparedPieceUpdate,_,_,_,_>
703     (
704       piece,by_client, gen_update,
705
706       |max_z, gpc| max_z.update_max(&gpc.zlevel),
707
708       |ioccults,gs,gpc,ipc,player,pri| {
709         let ops = match ops {
710           PUOs::Simple(update) => update,
711           PUOs::PerPlayer(ref ops) => match ops.get(player) {
712             Some(op) => *op,
713             None => return Ok(None),
714           }
715         };
716         let u = Self::piece_update_player(ioccults,gs,gpc,ipc,ops,pri)?;
717         Ok::<_,IE>(u)
718       },
719
720       |gpl| {
721         gpl.idmap.fwd(piece).map(
722           |vpid| PreparedPieceUpdate {
723             // The piece is deleted, so we can't leak anything.
724             piece: vpid,
725             op: PieceUpdateOp::Delete(),
726           }
727         )
728       }
729     )?;
730
731     PreparedUpdateEntry_Piece {
732       by_client: by_client.clone(),
733       ops
734     }
735   }
736
737   pub fn piece_update(&mut self, piece: PieceId,
738                       by_client: &IsResponseToClientOp,
739                       ops: PieceUpdateOps) {
740     // Caller needs us to be infallible since it is too late by
741     // this point to back out a game state change.
742
743     let update = self.piece_update_fallible(
744       piece,by_client, ops,
745       |pc, gen, by_client|
746     {
747       match *by_client {
748         Some((WRC::Predictable,tclient,_)) |
749         Some((WRC::UpdateSvg,  tclient,_)) => {
750           if tclient != pc.lastclient {
751             pc.gen_before_lastclient = pc.gen;
752             pc.lastclient = tclient;
753           }
754         }
755         Some((WRC::Unpredictable, _,_)) |
756           None => {
757             pc.lastclient = default();
758           }
759       }
760       pc.gen = gen;
761     })
762       .map(|update| PUE::Piece(update))
763       .unwrap_or_else(|e| {
764         error!("piece update error! piece={:?} error={:?}",
765                piece, &e);
766         PreparedUpdateEntry::Error(ErrorSignaledViaUpdate::InternalError)
767       });
768
769     let movehist_update = movehist::peek_prep_update(&mut self.g.gs, &update);
770
771     self.us.push(update);
772     self.us.extend(movehist_update);
773   }
774
775   #[throws(InternalError)]
776   pub fn piece_update_image(&mut self, piece: PieceId,
777                             by_client: &IsResponseToClientOp) {
778     // Use this only for updates which do not change the set of valid UOs
779     // or other operations or move the piece etc.
780     let ims = self.piece_update_fallible_players(
781       piece,by_client, |_,_,_|(), |_,_|(),
782
783       |ioccults,gs,gpc,ipc,_player,pri| {
784         let im = pri.as_ref().map(|pri| {
785           let im = pri.prep_pieceimage(ioccults,gs,gpc,ipc)?;
786           Ok::<_,IE>(PreparedPieceUpdateGeneral { piece: pri.vpid, op: im })
787         }).transpose()?;
788         Ok::<_,IE>(im)
789       },
790
791       |_gpl| None,
792     )?;
793     self.us.push(PUE::Image(PreparedUpdateEntry_Image { ims }))
794   }
795
796   pub fn piece_updates(&mut self,
797                        updates: Vec<(PieceId, PieceUpdateOps)>,
798                        by_client: &IsResponseToClientOp) {
799     for (piece, ops) in updates {
800       self.piece_update(piece, by_client, ops);
801     }
802   }
803
804   pub fn raw_updates(&mut self, mut raw: Vec<PreparedUpdateEntry>) {
805     self.us.append(&mut raw)
806   }
807
808   pub fn log_updates(&mut self, logents: Vec<LogEntry>) {
809     let now = Timestamp::now();
810     for logent in logents {
811       let when = iter::once(now).chain(
812         self.g.gs.log.back().map(|l| l.1.when)
813       ).max().unwrap();
814       let logent = Arc::new(CommittedLogEntry { when, logent });
815       let gen = self.gen();
816       self.g.gs.log.push_back((gen, logent.clone()));
817       self.us.push(PreparedUpdateEntry::Log(logent));
818     }
819   }
820
821   pub fn add_unprepared(&mut self, unprepared: UnpreparedUpdates) {
822     for suu in unprepared { suu(self); }
823   }
824
825   pub fn only_unprepared(ig: &'r mut Instance, unprepared: UnpreparedUpdates) {
826     Self::only_unprepared_with(unprepared, ||Ok::<_,Void>(ig))
827       .void_unwrap();
828   }
829
830   #[throws(E)]
831   pub fn only_unprepared_with<'i,F,E>(unprepared: UnpreparedUpdates, igf: F)
832   where F: FnOnce() -> Result<&'i mut Instance, E>
833   {
834     if unprepared.len() != 0 {
835       let ig = igf()?;
836       let mut prepub = PrepareUpdatesBuffer::new(ig, None);
837       prepub.add_unprepared(unprepared);
838       prepub.finish();
839     }
840   }
841
842   pub fn finish(self) { }
843 }
844
845 impl<'r> Drop for PrepareUpdatesBuffer<'r> {
846   fn drop(&mut self) {
847     if ! (self.us.is_empty() && self.gen.is_none()) {
848       let gen = self.gen();
849       let update = PreparedUpdate {
850         when: Instant::now(),
851         gen,
852         us: mem::take(&mut self.us),
853       };
854       let update = Arc::new(update);
855       trace!("PrepareUpdatesBuffer update {:?}", &update);
856
857       for (_tplayer, PlayerRecord { u: tplupdates, .. })
858         in &mut self.g.iplayers
859       {
860         tplupdates.push(update.clone());
861       }
862     }
863   }
864 }
865
866 // ---------- for traansmission ----------
867
868 impl PreparedUpdate {
869   pub fn for_transmit<'u>(&'u self, tz: &'u Timezone,
870                           player: PlayerId, dest: ClientId)
871                           -> TransmitUpdate<'u> {
872     type ESVU<T,EM> = ErrorSignaledViaUpdate<T,EM>;
873     type PUE = PreparedUpdateEntry;
874     type TUE<'u> = TransmitUpdateEntry<'u>;
875     let mut ents = vec![];
876
877     fn pue_piece_to_tue_p(pue_p: &PUE_P, player: PlayerId)
878                           -> Option<TUE_P> {
879       let op = pue_p.ops.get(player)?;
880       let PreparedPieceUpdate { piece, ref op } = *op;
881       Some(TUE_P { piece, op: op.map_ref() })
882     }
883
884     fn pue_image_to_tue_i(pue_i: &PUE_I, player: PlayerId) -> Option<TUE_I> {
885       let im = pue_i.ims.get(player)?;
886       let PreparedPieceUpdateGeneral { piece, ref op } = *im;
887       Some(TUE_I { piece, im: op })
888     }
889
890     fn pue_piece_to_tue(pue_p: &PUE_P, player: PlayerId, dest: ClientId)
891                         -> Option<TUE> {
892       let PUE_P { by_client, ref ops } = *pue_p;
893       let PreparedPieceUpdate { piece, ref op } = *ops.get(player)?;
894       let ns = ||op.new_state();
895       enum FTG<'u> {
896         Recorded(ClientSequence, Option<&'u PreparedPieceState>),
897         Exactly(TransmitUpdateEntry<'u>),
898         Piece,
899       }
900       let ftg = match by_client {
901         None                                     => FTG::Piece,
902         Some((_,u_client,_)) if u_client != dest => FTG::Piece,
903         Some((WRC::Predictable  ,_,cseq)) => FTG::Recorded(cseq, None),
904         Some((WRC::UpdateSvg    ,_,cseq)) => FTG::Recorded(cseq, ns()),
905         Some((WRC::Unpredictable,_,cseq)) => {
906           if let Some(ns) = ns() {
907             FTG::Exactly(TUE::RecordedUnpredictable { piece, cseq, ns })
908           } else {
909             error!("internal error: for_transmit PreparedUpdateEntry::Piece with RecordedUnpredictable but PieceOp no NS");
910             FTG::Piece
911           }
912         }
913       };
914       let tue = match ftg {
915         FTG::Recorded(cseq, ns) => {
916           let zg = op.new_z_generation();
917           TUE::Recorded {
918             piece, cseq, zg,
919             svg: ns.map(|ns| &ns.svg),
920             desc: ns.map(|ns| &ns.desc),
921           }
922         },
923         FTG::Piece => TUE::Piece(pue_piece_to_tue_p(pue_p, player)?),
924         FTG::Exactly(x) => x,
925       };
926       Some(tue)
927     }
928
929     for u in &self.us {
930       trace!("for_transmit to={:?} {:?}", dest, &u);
931       let ue = match u {
932         &PUE::Piece(ref pue_p) => {
933           match pue_piece_to_tue(pue_p, player,dest) {
934             Some(tue) => tue,
935             _ => continue,
936           }
937         }
938         &PUE::Image(ref pue_i) => {
939           match pue_image_to_tue_i(pue_i, player) {
940             Some(tue) => TUE::Image(tue),
941             _ => continue,
942           }
943         }
944         PUE::MoveHistEnt(ents) => {
945           match ents.get(player) {
946             Some(mhe) => TUE::MoveHistEnt(mhe),
947             _ => continue,
948           }
949         }
950         PUE::MoveHistClear => {
951           TUE::MoveHistClear{}
952         }
953         PUE::Log(logent) => {
954           TUE::Log((tz, logent))
955         }
956         &PUE::SetTableSize(size) => {
957           TUE::SetTableSize(size)
958         }
959         PUE::SetTableColour(colour) => {
960           TUE::SetTableColour(colour)
961         }
962         &PUE::SetPlayer { player, ref new_info_pane, ref data } => {
963           TUE::SetPlayer { player, new_info_pane, data }
964         }
965         &PUE::RemovePlayer { player, ref new_info_pane } => {
966           TUE::RemovePlayer { player, new_info_pane }
967         }
968         &PUE::UpdateBundles { ref new_info_pane } => {
969           TUE::UpdateBundles { new_info_pane }
970         }
971         PUE::Error(e) => {
972           match *e {
973             ESVU::InternalError => TUE::Error(ESVU::InternalError),
974             ESVU::PlayerRemoved => TUE::Error(ESVU::PlayerRemoved),
975             ESVU::TokenRevoked  => TUE::Error(ESVU::TokenRevoked),
976             ESVU::PieceOpError { error, partially,
977                                  ref error_msg, ref state } => {
978               let c    = state.by_client.as_ref().map(|(_,c,_   )| *c);
979               let cseq = state.by_client.as_ref().map(|(_,_,cseq)| *cseq);
980               if c == None || c == Some(dest) {
981                 let tue = match pue_piece_to_tue_p(state, player) {
982                   Some(tue) => tue,
983                   None => continue,
984                 };
985                 TUE::Error(
986                   ESVU::PieceOpError {
987                     error, error_msg, partially,
988                     state: { ETUE_P {
989                       tue,
990                       cseq,
991                     } },
992                   }
993                 )
994               } else {
995                 match partially {
996                   POEPP::Unprocessed => continue,
997                   POEPP::Partially => {
998                     match pue_piece_to_tue(state, player, dest) {
999                       Some(tue) => tue,
1000                       None => continue,
1001                     }
1002                   }
1003                 }
1004               }
1005             }
1006           }
1007         }
1008         PUE::SetLinks(links) => {
1009           TUE::SetLinks((&**links).into())
1010         }
1011       };
1012       ents.push(ue);
1013     };
1014     TransmitUpdate(self.gen, ents)
1015   }
1016 }
1017
1018 #[ext(pub)]
1019 impl Vec<(PieceId, PieceUpdateOps)> {
1020   fn into_unprepared(self, by_client: IsResponseToClientOp)
1021                      -> UnpreparedUpdates {
1022     if self.len() != 0 {
1023       vec![Box::new(
1024         move |buf: &mut PrepareUpdatesBuffer| {
1025           buf.piece_updates(self, &by_client)
1026         })]
1027     } else {
1028       default()
1029     }
1030   }
1031 }
1032
1033 impl<'u> From<TransmitUpdateLogEntry<'u>> for FormattedLogEntry<'u> {
1034   fn from(tule: TransmitUpdateLogEntry<'u>) -> FormattedLogEntry<'u> {
1035     let (tz, logent) = tule;
1036     let when = logent.when.render(tz);
1037     FormattedLogEntry { when, logent: &logent.logent }
1038   }
1039 }
1040
1041 fn serialize_logentry<S:Serializer>(&(tz,logent): &TransmitUpdateLogEntry,
1042                                     s:S) -> Result<S::Ok, S::Error> {
1043   let f: FormattedLogEntry = (tz, logent).into();
1044   f.serialize(s)
1045 }
1046
1047 pub const SYNCH_LOGENTRY_PREFIX: HtmlLit = Html::lit("### SynchLog ");
1048
1049 pub fn synch_logentry(gen: Generation) -> Html {
1050   hformat!("{}gen={} ###", SYNCH_LOGENTRY_PREFIX, gen.0)
1051 }