1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
5 // update messages from server to client
11 #[path="movehist.rs"] pub mod movehist;
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>;
17 // ---------- newtypes, type aliases, basic definitions ----------
19 #[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
20 #[derive(Serialize,Deserialize)]
22 pub struct UpdateId(i64);
24 pub type RawClientSequence = u64;
26 #[derive(Debug,Copy,Clone,Eq,PartialEq,Deserialize,Serialize)]
28 pub struct ClientSequence(RawClientSequence);
30 pub type UnpreparedUpdates = Vec<SomeUnpreparedUpdates>;
31 pub type SomeUnpreparedUpdates = Box<
32 dyn for<'r> FnOnce(&'r mut PrepareUpdatesBuffer)
35 // ---------- from manamgenet operations ----------
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>>,
44 // ---------- prepared updates, queued in memory ----------
46 pub type PlayerUpdatesLog =
47 StableIndexVecDeque<Arc<PreparedUpdate>,UpdateId>;
50 pub struct PlayerUpdates {
51 log: PlayerUpdatesLog,
56 pub struct PreparedUpdate {
59 pub us: Vec<PreparedUpdateEntry>,
63 pub enum PreparedUpdateEntry {
64 Piece(PreparedUpdateEntry_Piece),
65 Image(PreparedUpdateEntry_Image),
66 MoveHistEnt(SecondarySlotMap<PlayerId, movehist::Ent>),
69 SetTableColour(Colour),
70 SetLinks(Arc<LinksTable>),
74 new_info_pane: Arc<Html>,
78 new_info_pane: Arc<Html>,
81 new_info_pane: Arc<Html>,
83 Log(Arc<CommittedLogEntry>),
84 Error(ErrorSignaledViaUpdate<PUE_P, String>),
87 #[allow(non_camel_case_types)]
88 #[derive(Debug,Clone)]
89 pub struct PreparedUpdateEntry_Piece {
90 by_client: IsResponseToClientOp,
91 ops: SecondarySlotMap<PlayerId, PreparedPieceUpdate>,
94 pub type PreparedPieceUpdate = PreparedPieceUpdateGeneral<
95 PieceUpdateOp<PreparedPieceState, ZLevel>
98 #[allow(non_camel_case_types)]
99 #[derive(Debug,Clone)]
100 pub struct PreparedUpdateEntry_Image {
101 ims: SecondarySlotMap<PlayerId, PreparedPieceUpdateGeneral<
106 #[derive(Debug,Clone)]
107 pub struct PreparedPieceUpdateGeneral<U> {
108 piece: VisiblePieceId,
112 #[derive(Debug,Clone,Serialize)]
113 pub struct PreparedPieceState {
117 pub facehint: Option<FaceId>,
118 pub held: Option<PlayerId>,
121 pub angle: CompassAngle,
123 pub moveable: PieceMoveable,
124 pub rotateable: bool,
126 pub uos: Vec<UoDescription>,
127 pub occregion: Option<JsonString<Region>>,
131 #[derive(Debug,Copy,Clone,Serialize,Deserialize,Eq)]
132 #[derive(Ord,PartialEq,PartialOrd)]
133 pub enum PieceMoveable {
138 impl Default for PieceMoveable { fn default() -> Self { PieceMoveable::Yes } }
140 #[derive(Debug,Clone,Serialize)]
141 pub struct PreparedPieceImage {
145 pub uos: Vec<UoDescription>,
148 #[derive(Serialize,Debug)]
149 pub struct DataLoadPlayer {
154 // ---------- piece updates ----------
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> {
170 #[derive(From,Educe)]
171 #[educe(Default(bound="T: Default"))]
172 pub enum OpOutcomeThunkGeneric<A,T,E> {
175 /// Allows an operation full mutable access to the whole Instance.
177 /// Use with care! Eg, you might have to call save_game_and_aux_later.
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>>),
186 pub type OpOutcomeThunk = OpOutcomeThunkGeneric<
187 (PlayerId, PieceId), UpdateFromOpComplex, ApiPieceOpError>;
189 pub type OpHookThunk = OpOutcomeThunkGeneric<
190 (PlayerId,), UnpreparedUpdates, InternalError>;
192 pub type UpdateFromOpComplex = (
197 pub type PieceUpdateFromOpSimple = (
198 WhatResponseToClientOp,
199 PieceUpdateOp<(),()>,
202 pub type PieceUpdateResult = Result<PieceUpdate, ApiPieceOpError>;
205 pub struct PieceUpdate {
206 pub wrc: WhatResponseToClientOp,
207 pub log: Vec<LogEntry>,
208 pub ops: PieceUpdateOps,
212 pub enum PieceUpdateOps {
213 Simple(PieceUpdateOp<(),()>),
214 PerPlayer(PieceUpdateOps_PerPlayer),
217 #[allow(non_camel_case_types)]
218 pub type PieceUpdateOps_PerPlayer =
219 SecondarySlotMap<PlayerId, PieceUpdateOp<(),()>>;
221 impl From<PieceUpdateOp<(),()>> for PieceUpdateOps {
222 fn from(op: PieceUpdateOp<(),()>) -> Self { PUOs::Simple(op) }
225 impl From<PieceUpdateFromOpSimple> for PieceUpdate {
226 fn from((wrc, op, log): PieceUpdateFromOpSimple) -> Self {
227 PieceUpdate { wrc, log, ops: op.into() }
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(())) }
239 // ---------- for traansmission ----------
241 #[derive(Debug,Serialize)]
242 pub struct TransmitUpdate<'u> (
244 Vec<TransmitUpdateEntry<'u>>,
247 #[derive(Debug,Serialize)]
248 enum TransmitUpdateEntry<'u> {
250 piece: VisiblePieceId,
251 cseq: ClientSequence,
252 zg: Option<Generation>,
253 svg: Option<&'u Html>, // IsResponseToClientOp::UpdateSvg
254 desc: Option<&'u Html>,
256 Piece(TransmitUpdateEntry_Piece<'u>),
257 Image(TransmitUpdateEntry_Image<'u>),
258 MoveHistEnt(&'u movehist::Ent),
260 RecordedUnpredictable {
261 piece: VisiblePieceId,
262 cseq: ClientSequence,
263 ns: &'u PreparedPieceState,
266 SetTableColour(&'u Colour),
269 data: &'u DataLoadPlayer,
270 new_info_pane: &'u Html,
274 new_info_pane: &'u Html,
277 new_info_pane: &'u Arc<Html>,
280 #[serde(serialize_with="serialize_logentry")]
281 Log(TransmitUpdateLogEntry<'u>),
282 Error(ErrorSignaledViaUpdate<ETUE_P<'u>, &'u str>),
285 type TransmitUpdateLogEntry<'u> = (&'u Timezone, &'u CommittedLogEntry);
287 #[allow(non_camel_case_types)]
288 #[derive(Debug,Serialize)]
289 struct ErrorTransmitUpdateEntry_Piece<'u> {
290 cseq: Option<ClientSequence>,
292 tue: TransmitUpdateEntry_Piece<'u>,
294 #[allow(non_camel_case_types)]
295 type ETUE_P<'u> = ErrorTransmitUpdateEntry_Piece<'u>;
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>,
304 #[allow(non_camel_case_types)]
305 #[derive(Debug,Serialize)]
306 struct TransmitUpdateEntry_Image<'u> {
307 piece: VisiblePieceId,
308 im: &'u PreparedPieceImage,
311 #[derive(Debug,Serialize)]
312 struct FormattedLogEntry<'u> {
314 logent: &'u LogEntry,
317 // ========== implementation ==========
319 // ---------- helpful utilities ----------
322 pub fn log_did_to_piece_whoby(ioccults: &IOccults, goccults: &GOccults,
324 gpc: &GPiece, ipc: &IPiece, did: &str)
325 -> (Vec<LogEntry>, Option<Html>)
327 let who_by = by_gpl.nick.to_html();
328 let y = gpc.fully_visible_to_everyone();
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())?,
334 })().unwrap_or_else(|e|{
335 error!("failed to format during logging: {:?}", e);
336 Html::lit("<internal error>").into()
339 let log = vec![ LogEntry { html: hformat!(
349 pub fn log_did_to_piece(ioccults: &IOccults, goccults: &GOccults,
351 gpc: &GPiece, ipc: &IPiece, did: &str)
353 log_did_to_piece_whoby(ioccults,goccults,by_gpl,gpc,ipc,did)?.0
356 // ---------- support implementation ----------
358 impl Neg for UpdateId {
360 fn neg(self) -> Self { UpdateId(-self.0) }
363 impl Display for UpdateId {
364 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
365 Display::fmt(&self.0, f)
369 impl Bounded for UpdateId {
370 fn max_value() -> Self { UpdateId(Bounded::max_value()) }
371 fn min_value() -> Self { UpdateId(Bounded::min_value()) }
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)
380 fn index_output(&self, inner: usize) -> Option<Self> {
381 self.0.index_output(inner).map(UpdateId)
383 fn zero() -> Self { UpdateId(0) }
386 impl<A,T,E> OpOutcomeThunkGeneric<A,T,E> {
387 pub fn resolve(self, ig: &mut InstanceGuard, args: A) -> Result<T,E> {
389 OOTG::Immediate(uu) => Ok(uu),
390 OOTG::Reborrow(f) => f(ig, args),
395 // ---------- prepared updates, queued in memory ----------
397 pub struct PlayerUpdatesStartContext {
398 pub(self) u1: Arc<PreparedUpdate>,
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());
406 PlayerUpdates { log, cv }
411 pub fn start(gs: &GameState) -> PlayerUpdatesStartContext {
412 let u1 = Arc::new(PreparedUpdate {
414 when: Instant::now(),
417 PlayerUpdatesStartContext { u1 }
420 pub fn push<U: Into<Arc<PreparedUpdate>>>(&mut self, update: U) {
421 self.log.push_back(update.into());
422 self.cv.notify_all();
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() }
429 pub fn expire_upto(&mut self, before: Instant) {
431 if self.log.len() < 2 { break }
434 if let Some(front) = self.log.front() { front }
437 if front.when >= before { break }
439 trace!("update expiring #{}", self.log.front_index());
440 self.log.pop_front();
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>")
459 // ---------- PieceUpdatesOp ----------
461 impl<NS,ZC> PieceUpdateOp<NS,ZC> {
462 pub fn new_state(&self) -> Option<&NS> {
463 use PieceUpdateOp::*;
466 Insert (ns) => Some(ns),
467 InsertQuiet (ns) => Some(ns),
468 Modify (ns) => Some(ns),
469 ModifyQuiet (ns) => Some(ns),
471 MoveQuiet (_) => None,
472 SetZLevel (_) => None,
473 SetZLevelQuiet(_) => None,
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>
481 use PieceUpdateOp::*;
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)?),
494 pub fn map_ref(&self) -> PieceUpdateOp<&NS,&ZC> {
495 use PieceUpdateOp::*;
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),
509 F: FnOnce(NS) -> NS2,
511 > (self, f:F, g:G) -> PieceUpdateOp<NS2,ZC2>
514 |ns| <Result<_,Void>>::Ok(f(ns)),
515 |zc| <Result<_,Void>>::Ok(g(zc)),
518 pub fn new_z_generation(&self) -> Option<Generation>
519 where ZC: Borrow<ZLevel>
521 use PieceUpdateOp::*;
525 InsertQuiet (_) => None,
527 ModifyQuiet (_) => None,
529 MoveQuiet (_) => None,
530 SetZLevel (l) => Some(l.borrow().zg),
531 SetZLevelQuiet(l) => Some(l.borrow().zg),
536 pub struct PrepareUpdatesBuffer<'r> {
538 us: Vec<PreparedUpdateEntry>,
539 gen: Option<Generation>,
542 /// In PROTOCOL.md terms, None is a Server update
543 type IsResponseToClientOp = Option<(
544 WhatResponseToClientOp,
549 #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
550 pub enum WhatResponseToClientOp {
551 /// In PROTOCOL.md terms, a Client update
553 /// In PROTOCOL.md terms, a Client update which also updates
554 /// the visible piece image (which is just server-controlled).
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.
562 impl<'r> PrepareUpdatesBuffer<'r> {
563 pub fn new(g: &'r mut Instance, estimate: Option<usize>) -> Self
565 let us = estimate.map_or(vec![], Vec::with_capacity);
567 PrepareUpdatesBuffer {
574 pub fn spontaneous_image(g: &'r mut Instance,
576 estimate: Option<usize>)
578 let mut updates = PrepareUpdatesBuffer::new(g, estimate);
579 updates.piece_update_image(piece, &None)?;
583 pub fn gen(&mut self) -> Generation {
584 let gs = &mut self.g.gs;
585 *self.gen.get_or_insert_with(||{
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| {
603 POEPP::Unprocessed => { }
604 POEPP::Partially => { pc.gen = gen; pc.lastclient = default(); }
608 let update = PUE::Error(ESVU::PieceOpError {
609 error, state, partially,
610 error_msg: error.to_string(),
613 buf.log_updates(logents);
617 #[throws(InternalError)]
618 fn piece_update_player(ioccults: &IOccults,
622 op: PieceUpdateOp<(),()>,
623 pri: &Option<PieceRenderInstructions>)
624 -> Option<PreparedPieceUpdate>
626 let pri = match pri { Some(pri) => pri, None => return None };
629 let op = pri.map_piece_update_op(ioccults, gs, gpc, ipc, op)?;
630 op.map(|op| PreparedPieceUpdate {
637 #[throws(InternalError)]
638 fn piece_update_fallible_players<U,GUF,WMZ,MUF,MPF>
639 (&mut self, piece: PieceId, by_client: &IsResponseToClientOp,
645 -> SecondarySlotMap<PlayerId, U>
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>,
654 let gen = self.gen();
655 let gs = &mut self.g.gs;
656 let ioccults = &self.g.ioccults;
658 let mut gpc = gs.pieces.byid_mut(piece).ok();
659 let ipc = self.g.ipieces.get(piece);
661 if let Some(ref mut gpc) = gpc {
662 gen_update(gpc, gen, by_client);
664 let gpc = gs.pieces.byid(piece).ok();
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
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)?
686 if let Some(upd) = upd {
687 out.insert(player, upd);
693 #[throws(InternalError)]
694 fn piece_update_fallible<GUF>(&mut self, piece: PieceId,
695 by_client: &IsResponseToClientOp,
698 -> PreparedUpdateEntry_Piece
699 where GUF: FnOnce(&mut GPiece, Generation, &IsResponseToClientOp)
701 let ops = self.piece_update_fallible_players
702 ::<PreparedPieceUpdate,_,_,_,_>
704 piece,by_client, gen_update,
706 |max_z, gpc| max_z.update_max(&gpc.zlevel),
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) {
713 None => return Ok(None),
716 let u = Self::piece_update_player(ioccults,gs,gpc,ipc,ops,pri)?;
721 gpl.idmap.fwd(piece).map(
722 |vpid| PreparedPieceUpdate {
723 // The piece is deleted, so we can't leak anything.
725 op: PieceUpdateOp::Delete(),
731 PreparedUpdateEntry_Piece {
732 by_client: by_client.clone(),
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.
743 let update = self.piece_update_fallible(
744 piece,by_client, ops,
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;
755 Some((WRC::Unpredictable, _,_)) |
757 pc.lastclient = default();
762 .map(|update| PUE::Piece(update))
763 .unwrap_or_else(|e| {
764 error!("piece update error! piece={:?} error={:?}",
766 PreparedUpdateEntry::Error(ErrorSignaledViaUpdate::InternalError)
769 let movehist_update = movehist::peek_prep_update(&mut self.g.gs, &update);
771 self.us.push(update);
772 self.us.extend(movehist_update);
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, |_,_,_|(), |_,_|(),
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 })
793 self.us.push(PUE::Image(PreparedUpdateEntry_Image { ims }))
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);
804 pub fn raw_updates(&mut self, mut raw: Vec<PreparedUpdateEntry>) {
805 self.us.append(&mut raw)
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)
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));
821 pub fn add_unprepared(&mut self, unprepared: UnpreparedUpdates) {
822 for suu in unprepared { suu(self); }
825 pub fn only_unprepared(ig: &'r mut Instance, unprepared: UnpreparedUpdates) {
826 Self::only_unprepared_with(unprepared, ||Ok::<_,Void>(ig))
831 pub fn only_unprepared_with<'i,F,E>(unprepared: UnpreparedUpdates, igf: F)
832 where F: FnOnce() -> Result<&'i mut Instance, E>
834 if unprepared.len() != 0 {
836 let mut prepub = PrepareUpdatesBuffer::new(ig, None);
837 prepub.add_unprepared(unprepared);
842 pub fn finish(self) { }
845 impl<'r> Drop for PrepareUpdatesBuffer<'r> {
847 if ! (self.us.is_empty() && self.gen.is_none()) {
848 let gen = self.gen();
849 let update = PreparedUpdate {
850 when: Instant::now(),
852 us: mem::take(&mut self.us),
854 let update = Arc::new(update);
855 trace!("PrepareUpdatesBuffer update {:?}", &update);
857 for (_tplayer, PlayerRecord { u: tplupdates, .. })
858 in &mut self.g.iplayers
860 tplupdates.push(update.clone());
866 // ---------- for traansmission ----------
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![];
877 fn pue_piece_to_tue_p(pue_p: &PUE_P, player: PlayerId)
879 let op = pue_p.ops.get(player)?;
880 let PreparedPieceUpdate { piece, ref op } = *op;
881 Some(TUE_P { piece, op: op.map_ref() })
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 })
890 fn pue_piece_to_tue(pue_p: &PUE_P, player: PlayerId, dest: ClientId)
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();
896 Recorded(ClientSequence, Option<&'u PreparedPieceState>),
897 Exactly(TransmitUpdateEntry<'u>),
900 let ftg = match by_client {
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 })
909 error!("internal error: for_transmit PreparedUpdateEntry::Piece with RecordedUnpredictable but PieceOp no NS");
914 let tue = match ftg {
915 FTG::Recorded(cseq, ns) => {
916 let zg = op.new_z_generation();
919 svg: ns.map(|ns| &ns.svg),
920 desc: ns.map(|ns| &ns.desc),
923 FTG::Piece => TUE::Piece(pue_piece_to_tue_p(pue_p, player)?),
924 FTG::Exactly(x) => x,
930 trace!("for_transmit to={:?} {:?}", dest, &u);
932 &PUE::Piece(ref pue_p) => {
933 match pue_piece_to_tue(pue_p, player,dest) {
938 &PUE::Image(ref pue_i) => {
939 match pue_image_to_tue_i(pue_i, player) {
940 Some(tue) => TUE::Image(tue),
944 PUE::MoveHistEnt(ents) => {
945 match ents.get(player) {
946 Some(mhe) => TUE::MoveHistEnt(mhe),
950 PUE::MoveHistClear => {
953 PUE::Log(logent) => {
954 TUE::Log((tz, logent))
956 &PUE::SetTableSize(size) => {
957 TUE::SetTableSize(size)
959 PUE::SetTableColour(colour) => {
960 TUE::SetTableColour(colour)
962 &PUE::SetPlayer { player, ref new_info_pane, ref data } => {
963 TUE::SetPlayer { player, new_info_pane, data }
965 &PUE::RemovePlayer { player, ref new_info_pane } => {
966 TUE::RemovePlayer { player, new_info_pane }
968 &PUE::UpdateBundles { ref new_info_pane } => {
969 TUE::UpdateBundles { new_info_pane }
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) {
987 error, error_msg, partially,
996 POEPP::Unprocessed => continue,
997 POEPP::Partially => {
998 match pue_piece_to_tue(state, player, dest) {
1008 PUE::SetLinks(links) => {
1009 TUE::SetLinks((&**links).into())
1014 TransmitUpdate(self.gen, ents)
1019 impl Vec<(PieceId, PieceUpdateOps)> {
1020 fn into_unprepared(self, by_client: IsResponseToClientOp)
1021 -> UnpreparedUpdates {
1022 if self.len() != 0 {
1024 move |buf: &mut PrepareUpdatesBuffer| {
1025 buf.piece_updates(self, &by_client)
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 }
1041 fn serialize_logentry<S:Serializer>(&(tz,logent): &TransmitUpdateLogEntry,
1042 s:S) -> Result<S::Ok, S::Error> {
1043 let f: FormattedLogEntry = (tz, logent).into();
1047 pub const SYNCH_LOGENTRY_PREFIX: HtmlLit = Html::lit("### SynchLog ");
1049 pub fn synch_logentry(gen: Generation) -> Html {
1050 hformat!("{}gen={} ###", SYNCH_LOGENTRY_PREFIX, gen.0)