chiark / gitweb /
changelog: Fix heading for 1.1.0
[otter.git] / PROTOCOL.md
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
6 CONCURRENT UPDATE PROTOCOL AND ANALYSIS
7 =======================================
8
9 (Assumption: our network techniques deliver messages in order in both
10 directions between a server and a client; failure of the communication
11 stream is allowed to break things - we treat it as fatal.)
12
13 We run the following algorithm / data model separately for each piece.
14
15
16 Model
17 -----
18
19 There is:
20
21   * actual committed history, a list of updates
22   * the history seen so far by the client
23     this can only advance, which is a prefix of the above
24   * possibly branch(es) of history made by the client;
25     this can rewind and be discarded
26
27 There is a digraph of updates.  Each update specifies the complete
28 state of the piece.  The server maintains an authoritative history;
29 the client only maintains the state of the piece.
30
31 Updates may be from this client ("`Client` updates") or from another
32 client or made by the server itself.  We look at the protocol from a
33 single client's point of view, and call all the other updates
34 "`Server` updates".
35
36 Each update has a single direct ancestor, but this is not recorded in
37 the message nor by the client.  Updates may be some combination of:
38
39   * `Recorded` - stored by server.
40   * `Downbound` - on way from server to client.
41   * `Discarded` - received by server and thrown away.
42   * `Upbound` - on way from client to server.
43   * `Displayed` - seen by client, is most recent state there
44   * `Superseded` - seen by client, is no longer most recent state.
45
46 Server assigns generation number (strictly increasing but maybe with
47 gaps) to each `Recorded` update.
48
49 Each `Upbound` update is tagged with the generation of the most recent
50 Processed update.
51
52 (Actually the most recent Processed generation stored by the client is
53 global across all pieces, and the server's generations are likewise
54 generated globally although recorded separately for each piece.  So
55 the update contain s a generation >= the most recently Processed
56 update for that piece, and < any subseuent Processed upate for that
57 piece.  The server only compares the incoming update generation for >=
58 with a recorded value, so this makes no difference.)
59
60 Desirable properties and invariants
61 -----------------------------------
62
63 Legal combinations are:
64
65   * `Server` + `Recorded` + `Downbound`
66   * `Server` + `Recorded` + `Displayed`
67   * `Server` + `Recorded` + `Superseded`
68   * `Client` + (`Displayed` / `Superseded`) + `Upbound`
69   * `Client` + (`Displayed` / `Superseded`) + `Recorded`
70   * `Client` + (`Displayed` / `Superseded`) + `Discarded`
71
72 _linear_: The `Recorded` updates form a linear sequence (so it is only
73 ever appended to).
74
75 _forwards_: Each update has at most one `Server` descendant, and at most
76 one `Client` descendant.
77
78 _display_: Exactly one update is `Displayed` at any one time.  It has no
79 `Client` child and no `Superseded` descendants.  Each update is only
80 `Displayed` at most once, and can only become `Superseded` after having
81 been `Displayed`.
82
83 _discard_: For every `Discarded` update K there is a `Recorded` `Server`
84 update S which not an ancestor of K; and with S's generation > K's
85 tag.
86
87 _record_: Sequence of `Recorded` updates is as follows:
88
89   * `Recorded` `Superseded` (zero or more)
90   * `Recorded` `Displayed` (zero or one)
91   * `Recorded` `Downbound` (zero or more)
92
93
94 Update generation operations
95 ----------------------------
96
97 ### I. Server update ###
98
99 Server may invent and transmit an update at any time.  Its ancestor is
100 the most recent `Recorded` update.  It is `Recorded`+`Downbound`.  No other
101 updates change their state.
102
103 Properties and invariants: _linear_, _forwards_, _record_ are
104 obvious.  _display_ is not affected.
105
106 ### II. Client update ###
107
108 Client may invent and transmit an update at any time; it must have the
109 `Displayed` update as its parent.  That update becomes `Superseded`.
110 The new update is `Displayed`+`Upbound`.
111
112 _linear_ and _record_ are not affected.  _forwards_ is honoured
113 because of _display_.  _display_ is preserved.
114
115 Server message reception
116 ------------------------
117
118 Suppose the server receives a message, Q.  It was `Client` `Upbound`.
119
120 Let T be the most recent `Recorded` message.  The server finds the most
121 recent `Server` `Recorded` message, U.
122
123 ### I. Q's generation is >= U's generation. ###
124
125 The server records Q with T as parent.  Q becomes `Client` `Recorded`; it
126 is `Displayed` or `Superseded` as before.
127
128 _linear_: We need to show that T really is Q's parent.
129
130 If Q's generation >= U's generation, the ancestry path <U..Q] exists
131 and contains only `Client` updates: if there were some `Server` update S
132 in U..Q, it would be `Recorded` (since all `Server` messages are) and more
133 recent than U, contradicting the definition of U.
134
135 Because of in-order message delivery, all updates in <U..Q> have been
136 received by the server.  So they must be `Recorded` or `Discarded`.
137
138 Suppose one, K, was `Discarded`.  Then by _discard_ there is some
139 `Recorded` `Server` update S which is not an ancestor of Q.  S's
140 generation must be > Q's tag, since Q's tag is >= all of its `Recorded`
141 ancestors.  So S's generation > U's.  But this means that S is a
142 `Server` update more recent than U. _|_
143
144 So everything in <U..Q> is `Recorded`.  Only Q is not `Recorded`.
145 Therefore Q's parent is indeed the most recent `Recorded` update, T.
146
147 _forwards_: We aren't making new messages.  _display_: We aren't
148 changing any of this.  _discard_: we aren't discarding anything.
149
150 _record_: Let S be the most recent `Recorded` `Downbound` update S.  This
151 must be U.  But Q's generation >= U's generation so U can no longer be
152 `Downbound`.  So there are no `Downbound` updates.  Maybe Q is `Displayed`;
153 in any case all older `Recorded` updates are `Superseded`.
154
155 ### II. Q's generation is < U's generation ###
156
157 The server discards Q.  It becomes `Discarded`.
158
159 _linear_, _forwards_, _display_, _record_: No change.
160
161 _discard_: Q becomes K.  Use U as S.  U is `Recorded`.  If Q had U as an
162 ancestor, U's generation would have been visible to the client when it
163 generated Q, so Q's tag would have been >= U's generation.  S's
164 generation is U's generation and is >= Q's by case assumption.
165
166
167 Client message reception
168 ------------------------
169
170 Suppose the client receives a message Q.
171
172 It displays it.  (The currently `Displayed` update becomes `Superseded`.
173 Q becomes `Displayed`.)
174
175 _linear_, _forwards_, discard_: No change.
176
177 _display_: Q is a `Server` update because it was `Downbound`.  We have
178 just received it so it cannot yet have any `Client` descendant.  It
179 might have `Server` descendants, but those are also `Recorded` `Downbound`,
180 by _record_.
181
182 _record_: By order of delivery, Q is the most recent `Recorded`
183 `Downbound`.
184
185
186 Liveness property
187 -----------------
188
189 "When things ahve settled, all is consistent."
190
191 If there are no `Upbound` or `Downbound` updates, the most recent `Recorded`
192 update R is `Displayed`.
193
194 Suppose some update D is displayed instead.
195
196 D can't be `Recorded`.  If it were, R would have to be `Downbound`, by
197 _record_.
198
199 So D can't be `Server`.  Suppose D is `Client`.  It must be `Recorded`,
200 `Upbound` or `Discarded`.  Only `Discarded` is left.
201
202 If D is `Discarded`, by _discard_, there is some `Recorded` `Server` S which
203 is not an ancestor of D, with S's generation > D's.  S must be
204 `Displayed` or `Superseded`, since there are no `Downbound` updates.  If S
205 were `Displayed` it would be equal to D, hence D would be an ancestor of
206 S.  So S must be `Superseded`.
207
208 If S is `Recorded` `Superseded`, at some point it was `Displayed`.  D is now
209 `Displayed`.  So D was `Displayed` more recently than S.  D is `Client` so
210 when it was `Displayed` it became `Upbound`, tagged with a generation of
211 at least S's; i.e. D's generation >= S's.  _|_
212
213
214 SERVER ACKS
215 ===========
216
217 Actually, the client wants to know when conflicts occur, so that it
218 can report to the user, and interrupt drag operations etc.
219 To this end:
220
221 Update messages from the client to the server also contain a client
222 sequence number (monotonically but not strictly increasing).  When the
223 client sends an update, it makes a note of the sequence number.  This
224 is the "outstanding Client updates sequence number note", or the
225 **oustanding note**.
226
227 When the server processes a message from the client and Records it, it
228 puts the client sequence number in the update stream (as a non-update
229 message).  (This is skipped if it's the same as the last client
230 sequence number for that client.)
231
232 If the echoed sequence number is equal to the client's _oustanding
233 note_, the client knows the server is up to date, and deletes the note.
234
235 If the client sees a Server update message, and the client has a
236 _note_, it knows that there was a conflict.
237
238
239 LEVEL (Z ORDER)
240 ===============
241
242 Each piece has a Z level (an arbitrary-precision value in <0,1>), set
243 by the client which manipulates the piece, according to the protocol
244 above.
245
246 Each piece *also* has a Z level generation.  This is set by the
247 server.  The server guarantees to set it to the server generation, and
248 guarantees to do so as the result of any client Z level update.  
249
250 So the client which sends a Z level update can assume that a server
251 update to the generation will turn up, and with a higher value.
252
253 The Z generation is used to disambiguate the Z order for pieces with
254 identical Z level.  Higher values are closer to the user (ie, occlude
255 lower values).
256
257
258 ERRORS
259 ======
260
261 Most of the time, the client will be able to predict what the server's
262 response to an update will be.  So usually, a `Client` `Upbound`
263 update will become `Recorded`, unless there was a conflict.
264
265 However, sometimes this is not the case: a piece can have behaviours
266 which are too complicated to model in the client.  In such cases, the
267 server will be processing a `Client` `Upbound` update, and find that
268 it cannot be applied and made `Recorded`.  (Call that an
269 **inapplicable** update.)
270
271 When the server receives an update it deems _inapplicable_, it will
272 generate a `Server` update and process that before the incoming
273 `Client` update.  The `Server` update will inform the client of the
274 problem, and, by its existence cause the `Client` _inapplicable_ update
275 to become `Discarded`.  When the client receives the `Server` update,
276 it can describe the problem to its user, and also note that the
277 `Client` update has become `Superseded`.
278
279 Additionally, of course, the client might have bugs (or be malicious).
280 So there can also be `Client` updates which the client ought to know
281 are **bogus**.  This includes syntactically malformed updates, but it
282 could also include updates with semantic errors.
283
284 _Bogus_ updates might not fit into the update synchronisation
285 protocool.  Since they arrive by HTTP, the server can reject them with
286 an HTTP error code.  A non-malicious client can deal with that by
287 reporting the problem immediately to its user (although
288 synchronisation will be lost).
289
290 In theory the server might be able to predict precisely what the
291 client can know, and precisely distinguish between `Client` updates
292 which are unprocessable because of something only known by the server,
293 and ones which are unprocessable because of misbehaviour (eg, bugs) at
294 the client.  However, getting this right is very fiddly and complex.
295
296 In the absence of buggy or malicious clients, there will be no _bogus_
297 updates.  And since even _inapplicable_ updates are rejected by the
298 server, there is no harm to anyone else of treating an update as
299 _inapplicable_ rather than _bogus_.  So we err on the side of treating
300 client mistakes as due to synchronisation and incomplete modelling:
301 ie, we only treat a client update as _bogus_ if it is patently
302 obviously wrong (for example, it is syntactically invalid).
303
304 Fatal errors which the client could not have predicted, but which are
305 not recoverable, can also be treated as bogus.
306
307
308 REGRAB
309 ======
310
311 Additionally, there is one special kind of operation, which allows a
312 limited degree of desynchronisation between client and server.
313
314 We want that player who has just released a piece, may immediately
315 regrasp it, even if concurrently there is a `Server` update in flight
316 which is the result of the release.  This is because drawing a card
317 from a pickup deck does not reveal the card until the player releases
318 it, but the player will often want to immediately play the card.
319
320 The server will only honour a regrab update if it comes from the same
321 client as the last client to ungrab the piece.
322
323 Formally this introduces a new flag on `Client` updates: whether they
324 are, **`Loose`**.  The client which generates an update decides
325 whether it is `Loose`.
326
327 When the server receives a `Loose` update which seems to be out of
328 date it will decide (using its own algorithms) whether it should still
329 be processed, and if so, what that means.  If it likes the update, it
330 will generate a `Server` update implementing its decision.
331
332 The client's _outstanding note_ records whether the outstanding branch
333 is precisely a `Loose` update.  If so, `Server` updates do not clear
334 the note until a `Server` message is received acknowledging that
335 client sequence.
336
337 After `Display`ing a server update, when there is an outstanding
338 `Loose` `Client` update, the client may, but is not required to,
339 "replay" the effect of the `Loose` update, to show its user what the
340 predicted effect will be.  Likewise it may discard the note of the
341 outstanding `Loose` update if it thinks it will inevitably generate a
342 conflict.