3 Empire is designed as a smart server with dumb clients. An Empire
4 client need to know nothing about the game. Even telnet would do. In
5 fact, empire-client is little more than a slightly specialized telnet.
7 In such a design, presentation is in the server, and it is designed
8 for human consumption. Ideally, presentation and logic are cleanly
9 separated, for easy selection between different presentations.
10 There's no such separation in the Empire server, and separating them
11 now would be a herculean effort.
13 Thus, smart clients have to work with output designed for humans.
14 That's not especially hard, just an awful lot of tedious work, and the
15 result gets easily broken by minor changes in output format, which
18 Instead of making smart clients parse output of commands designed for
19 humans, one can add commands designed for machines. Such commands can
20 share the code implementing game rules with their counterparts for
21 humans. To do that cleanly means separating logic and presentation.
22 Implementing them with their own copy of the code is no good --- how
23 would you ensure that the two copies implement precisely the same game
26 Except for commands that actually don't do anything. There's a useful
27 class of such commands: commands to show game configuration and state
28 without altering it. The only game rules involved are those that
29 govern who gets to see what. Ensuring that those are obeyed is
32 Empire has had one such command since the beginning: dump. Empire
33 4.0.6 added more: sdump, ldump, pdump and ndump. 4.0.7 added lost and
34 support for incremental dumps. These commands have served smart
35 clients well. However, they cover just the most important part of the
36 game state (sectors, ships, planes, land units, nukes), and no game
37 configuration. They are not quite complete even for what they attempt
38 to cover. Finally, their output is harder to parse than necessary.
40 The xdump command is designed to be the dump to end all dumps.
42 Like many good ideas, xdump has secondary uses. Dumping game state as
43 text can be one half of a game export/import facility. Useful to
44 migrate games to different machines or even, with some text mangling
45 perhaps, different server versions. We will see below that xdump
46 isn't quite sufficient for that, but it's a start.
48 Means to import game configuration let you customize your game without
49 recompiling the server. As we will see, configuration files have
50 different requirements, which xdump doesn't satisfy without some
53 If game import code can edit everything, then a deity command capable
54 of editing everything is possible. Proof-of-concept code exists (not
58 Analysis of the data to dump
60 Game state consists of a fixed set of table files (sector, ship, ...),
61 telegram files, and a few miscellaneous files. Game configuration
62 consists of a fixed set of configuration tables and scalar parameters.
64 A table is an ordered set of records (table rows). All records have
65 the same fields (table columns), which are statically typed.
67 Fields may be integers, floating-point numbers, x- or y-coordinates,
68 symbols and symbol sets encoded as integers in a way specific to the
69 server version, and character arrays. Configuration table fields may
70 be pointers to zero-terminated strings, null pointers allowed. No
71 other pointers occur. Unions do not occur.
78 * Capable to dump all tables.
80 * Can do incremental dumps.
84 * Output is reasonably compact.
86 * Output is trivial to parse. Triviality test: if it's easy in AWK, C
87 (no lex & yacc, just stdio), Lisp (just reader) and Perl (base
88 language, no modules), then it's trivial enough.
90 * Output identifies itself.
92 * Output is self-contained; symbol encoding is explicit.
94 * KISS: Keep it simple, stupid.
98 * Generality. We're not trying to design a general mechanism for
101 * Completeness. We're not trying to dump stuff other than tables.
103 * Abstraction. We're not trying to hide how things are stored in the
104 server. When storage changes, xdump output will change as well, and
105 consumers need to be updated. This is not because abstraction
106 wouldn't be nice to have, just because we don't feel up to the task
112 Traditional dumps have a dump function for every table. These
113 functions are simple, but exceedingly dull and repetitive.
115 The selector code works differently. Each table has a descriptor,
116 which among other things defines a dictionary of selector descriptors.
117 A selector descriptor describes a field (table column) visible to
118 players. This is what we call meta-data (data about data). The
119 selector code knows nothing about the individual tables, it just
120 interprets meta-data. That's smart, as it keeps the dull, repetitive
121 parts in more easily maintainable meta-data rather than code.
123 xdump follows the selector design, and uses the existing selector
124 meta-data. This requires extending the meta-data to configuration
125 tables, which weren't previously covered. It also requires some
126 generalization of selector descriptors, so that all fields can be
129 To sum up, meta-data consists of a table of tables, and for each table
130 a table of selectors (table of columns, so to speak). It is specific
131 to the server version and how it is compiled on the host.
133 To interpret a table xdump, you need its meta-data, because without it
134 you have no idea what the columns mean. As meta-data is just a bunch
135 of tables, xdump can dump it. But now you need meta-meta-data to make
136 sense of the meta-data. Fortunately, meta-meta-data is the same for
137 all xdumps, and therefore the recursion terminates with a single
140 xdump dumps symbols and symbol sets as integers. To decode them, you
141 need to know what the symbol numbers and symbol set bits mean. For
142 this purpose, field meta-data includes the table ID of a symbol table.
143 A symbol table is a table of value-name pairs, with the value in the
144 leftmost column. You decode a symbol value by looking it up in the
145 symbol table. You decode a symbol set value by looking up its bits
146 (powers of two) in the symbol table.
148 Some integer fields are actually keys in other tables. For instance,
149 ship field type is a key in the table of ship types ship-chr, and
150 plane field ship is a key in the ship table. Key -1 is special: it's
151 a null key. Meta-data encodes these table reference just like for
152 symbols: the meta-data has the ID of the referenced table, and that
153 table has the key in the leftmost column. Obviously, that leftmost
154 column is a table key as well, referencing the table itself.
156 A table with its key in the leftmost column can be dumped partially.
157 Without such a key, you need to count records to find the record
158 index, and that works only if you can see a prefix of the complete
162 Syntax of xdump command
167 The xdump output Language
169 Because the output is to be parsed by machines, it needs to be
170 precisely specified. We use EBNF (ISO 14977) for syntax, except we
171 use '-' in meta-identifiers and omit the concatenation symbol ','.
173 table = header { record } footer ;
174 header = "XDUMP" space [ "meta" space ]
175 identifier space timestamp newline ;
176 identifier = id-chr { id-chr } ;
177 id-char = ? ASCII characters 33..126 except '"#()<>=' ? ;
179 footer = "/" number newline ;
180 record = [ fields ] newline ;
181 fields = field { space field } ;
182 field = intnum | flonum | string ;
183 intnum = ? integer in printf %d format ? ;
184 flonum = ? floating-point in printf %g format ? ;
186 | '"' { str-char } '"' ;
187 str-char = "\\" octal-digit octal-digit octal-digit
188 | ? ASCII characters 33..126 except '"' and '\\' ? ;
189 octal-digit = ? '0'..'7' ? ;
191 newline = ? ASCII character 10 ? ;
195 * The syntax for flonum is debatable. Precise conversion between
196 floating-point and decimal is hard, and C libraries are not required
197 to be precise. Using C99's %a format for flonum would avoid the
198 issue, but some programming environments may have trouble converting
199 that back to floating-point. We may change to %a anyway in the
200 future. Clients are advised to accept both.
202 * Strings syntax could perhaps profit from the remaining C escape
203 sequences. Except for '\"': adding that would complicate regular
204 expressions matching the string, and thus violate the `trivial to
207 * Space is to be taken literally: a single space character. Not a
208 non-empty sequence of white-space.
212 * The table identifier in the header is one of the names in xdump table.
214 * The timestamp increases monotonically. It has a noticeable
215 granularity: game state may change between an xdump and the next
216 timestamp increase. If the table has a timestamp field, clients can
217 xdump incrementally by using a conditional ?timestamp>T, where T is
218 one less than the timestamp received with the last xdump of that
221 Timestamp values are currently seconds since the epoch, but this
222 might change, and clients are advised not to rely on it.
224 * The number in the footer matches the number of records.
226 * Fields match their meta-data (see Meta-Data below).
228 * "nil" represents a null string (which is not the same as an empty
229 string). Otherwise, fields are to be interpreted just like C
235 Table meta-data is in xdump table. Fields:
237 * uid: The table ID, key for xdump table. IDs depend on the server
238 version; clients should not hard-code them. This is the leftmost
241 * name: The table name. Clients may identify tables by name.
243 Field meta-data for table T is in xdump meta T. The order of fields
244 in the xdump T matches the order of records in xdump meta T. Fields
247 * name: The field name. Matches the selector name. Clients may
248 identify fields by name. This is the leftmost field.
250 * type: The field's data type, a symbol. Clients should use this only
251 as key for the symbol table. Symbols are:
252 - "d", field uses intnum syntax
253 - "g", field uses flonum syntax
254 - "s", field uses string syntax
255 - "c", field uses string syntax
257 * flags: The field's flags, a symbol set. Flags are:
258 - "deity", field visible only to deities
259 - "extra", field not to be dumped
260 - "const", field cannot be changed
261 - "bits", field is a symbol set, field type must encode symbol "d",
262 field table must not be -1.
264 * len: If non-zero, then the record encodes an array with that many
265 elements. If field type encodes symbol "c", it is a character
266 array, which is dumped as a single string field. Else, the array is
267 dumped as len fields.
269 * table: Key for xdump table. Unless -1, it defines the table
270 referenced by the field value. Field type must encode symbol "d"
275 * value: The symbol's encoding as integer. If the symbol can be
276 element of a symbol set, this is a power of two.
278 * name: The symbol's name.
281 Notes on xdump Implementation
283 Overall impact on the server code is low.
285 To keeps xdump simple, storage of game state and game configuration
286 tables has been unified under the common empfile abstraction, making
287 nxtitem-iterators and selectors equally applicable to all tables.
289 xdump required a few extensions to meta-data, which may become useful
290 in other places as well:
292 * Selectors can now deal with arrays (revived struct castr member
293 ca_len). Not yet available on the Empire command line.
295 * Selector meta-data can now express that a selector value is a key
296 for another table (new struct castr member ca_table). The selector
297 code doesn't use that, yet.
299 * Selector flag NSC_EXTRA to flag redundant selectors, so that xdump
302 Meta-data is in empfile[] (table meta-data), src/lib/global/nsc.c
303 (selector meta-data), src/lib/global/symbol.c (symbol tables). The
304 command is in src/lib/commands/xdump.c, unsurprisingly.
307 Hints on Using xdump in Clients
309 Let's explore how to dump a game. To make sense of a table, we need
310 its meta-data, and to make sense of that table, we need meta-meta
311 data. So we start with that:
313 [14:640] Command : xdump meta meta
314 XDUMP meta meta 1139555204
322 To interpret this table, we have to know the field names and their
323 meanings. Clients hard-code them. They should be prepared to accept
324 and ignore additional fields, and to cope with changes in field order,
325 except they may rely on "name" coming first.
327 A word on hard-coding. Clients hard-code *names*. The numbers used
328 for table IDs and to encode symbols are none of the client's business.
330 The encoding doesn't normally change within a game. Except when the
331 game is migrated to a sufficiently different server. That's a rare
332 event. Clients may wish to provide for such changes anyway, by
333 decoupling the client's encoding from the server's, and dumping fresh
334 meta-data on login. Incremental meta-data dump would be nice to have.
336 So we don't know how symbol type and symbol set flags are encoded. To
337 decode them, we need their symbol tables. However, we need flags and
338 type only for tables we don't know, and there's one more table we do
339 know, namely the table of tables. Let's dump that next, starting with
342 [31:640] Command : xdump meta table
343 XDUMP meta table 1139556230
348 Because xdump table is referenced from elsewhere (xdump meta meta
349 field table), the leftmost field must contain the key. Thus, the
350 leftmost field's meta-data field table must be the table ID of xdump
351 table itself. Let's try it:
353 [30:640] Command : xdump 26 *
354 XDUMP table 1139556210
369 Now dump the two symbol tables we postponed. Because xdump accepts
370 table IDs as well as names, we don't have to know their names:
372 [14:640] Command : xdump meta 32
373 XDUMP meta meta-type 1139555298
378 [15:640] Command : xdump 32 *
379 XDUMP meta-type 1139555826
396 [15:640] Command : xdump meta 33
397 XDUMP meta meta-flags 1139555303
402 [24:640] Command : xdump 33 *
403 XDUMP meta-flags 1139555829
410 We now have complete meta-meta information:
412 name type flags len table
413 -----------------------------------------
415 type d (const) 0 meta-type
416 flags d (bits const) 0 meta-flags
420 Dumping the remaining tables is easy: just walk the table of tables.
421 Here's the first one:
423 [36:640] Command : xdump meta 0
424 XDUMP meta sect 1139556498
432 A whole load of tables referenced! Only one of them (not shown above)
435 owner references table nat. No surprise.
437 xloc and yloc together reference the sector table, but that's not
438 expressed in meta-data (yet).
440 Let's stop here before this gets too long and boring. Experiment
441 yourself! Check out example Perl code scripts/xdump.pl.
444 Analysis of xdump as Configuration File Format
446 xdump makes a lousy configuration format because it is unwieldy to
447 edit for humans. That's because configuration files have different
448 requirements than dumps:
450 * Can be edited by humans with common tools, including text editors
453 Using text editors requires a nice fixed-width table layout.
454 Spreadsheet import requires trivial field separation. Tab character
455 field separator or fixed width columns should do. The syntax should
456 allow all that, but not require it.
460 - xdump's rigid horizontal and vertical spacing makes it impossible
461 to align things visually.
463 - xdump uses one line per record, which can lead to excessively long
466 - xdump's string syntax requires octal escape for space.
470 * Each table is self-contained. You don't have to look into other
471 tables to make sense of it.
473 This conflicts with xdump's separation of data and meta-data. You
474 need the table's meta-data to identify fields, and the referenced
475 symbol tables to decode symbols.
477 * Easy to parse. Don't compromise legibility just to please some dumb
480 Since we're trying to apply xdump to the configuration file problem,
481 we get an additional requirement:
483 * Reasonably close to xdump. Translation between machine-readable and
484 human-readable should be straightforward, if meta-data is available.
486 This leads to a human-readable dialect of the xdump language.
489 Human-Readable xdump Language
491 Fundamental difference to basic, machine-readable xdump: the rigid
492 single space between fields is replaced by the rule known from
493 programming languages: white-space (space and tab) separates tokens
494 and is otherwise ignored. The space non-terminal is no longer needed.
496 Rationale: This allows visual alignment of columns and free mixing of
497 space and tab characters.
499 Comments start with "#" and extend to the end of the line. They are
500 equivalent to a newline.
502 Rationale: Follow econfig syntax.
504 Tables with a record uid in the leftmost field can be `split
505 vertically' into multiple parts. Each part must contain the same set
506 of records. The leftmost field must be repeated in each part. Other
507 fields may be repeated. Repeated fields must be the same in all
508 parts. Naturally, the parts together must provide the same fields as
509 a table that is not split.
511 Rationale: This is to let you avoid long lines. Line continuation
512 syntax would be simpler, but turns out to be illegible. Requiring
513 record uid is not technically necessary, as counting records works the
514 same whether a table is split or not. Except humans can't count.
515 Perhaps this should be a recommendation for use rather than part of
522 header = "config" identifier newline colhdr newline ;
523 colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ;
524 footer = "/config" newline ;
526 If colhdr ends with "...", the table is continued in another part,
527 which shall follow immediately.
531 - The xdump needs to identify itself as human-readable, hence change
532 from "XDUMP" to "config".
534 - The timestamp in the header is useless for the applications we
535 have in mind for human-readable xdumps. The number of records in
536 the footer is of marginal value at best, and a pain for humans to
539 - The column header is due to the self-containedness requirement.
540 It contains just the essential bit of meta-data: the column names.
544 field = intnum | flonum | string | symbol | symset ;
548 - Syntax for symbols and sets of symbols is due to the
549 self-containedness requirement. Machine-readable xdump gets away
550 with just numbers, which have to be decoded using meta-data.
552 * Friendlier numbers and strings:
554 flonum = ? floating-point in scanf %g format ? ;
555 str-char = "\\" octal-digit octal-digit octal-digit
556 | ? ASCII characters 32..126 except '"' and '\\' ? ;
560 - Machine-readable floating-point syntax is too rigid. Accept
561 everything that scanf does. Could also change intnum to %i
562 format, which accepts octal and hexadecimal in C syntax, but that
563 seems not worth the documentation bother.
565 - Machine-readable syntax requires \040 instead of space in strings
566 to allow trivial splitting into fields. This is unacceptable here
567 due to the legibility requirement, hence the change to str-char.
569 * Parse nil as symbol:
571 string = '"' { str-char } '"' ;
573 Rationale: This is a technicality required to keep the parse
578 symbol = identifier ;
579 symset = "(" { symbol } ")" ;
581 The special symbol "nil" is to be interpreted as null string.
585 - The symbol set syntax is the simplest that could work. We need to
586 allow space between the symbols for legibility anyway, so why not
587 make it the delimiter. A stop token is required to find the end
588 of the field, and a start token is useful for distinguishing
589 between symbol and symset. Bracketing with some kind of
590 parenthesis is an obvious solution.
592 The resulting sub-language for records is a superset of
593 machine-readable sub-language for records.
595 See src/lib/global/*.config for examples.
598 Notes on Table Configuration Implementation
600 econfig key custom_tables lists table configuration files. At this
601 time, reading a custom table merges it with the built-in table, then
602 truncates the result after the last record read from the custom table.
604 Some of the tables are rather ugly in C, and cumbersome to edit. We
605 thus moved them to configuration files (src/lib/global/*.config). The
606 server reads them from builtindir before reading custom tables.
608 The code dealing with these files is in src/lib/common/conftab.c.
610 Actual work is done by src/lib/common/xundump.c, which accepts both
611 human-readable and machine-readable input. The parser is not precise;
612 it accepts human-readable syntax even within tables whose header marks
613 them machine-readable.
615 Symbolic index values in column headers are not implemented. They
616 occur in item selector pkg, which is an array indexed by values in
617 symbol table packing.
619 Configuration tables contain values that are not meant to be
620 customized. For instance, meta-data and symbol tables reflect the
621 encoding of C language constructs in the server. Selector flag
622 NSC_CONST marks them, so that the code can prohibit changes.
624 All tables are checked against meta-data on server startup by
625 ef_verify(). More elaborate checking would be nice, and probably
626 requires additional meta-data.
629 Appendix: Empire 3 C_SYNC --- A Cautionary Tale
631 Clients are just as important as the server, and it's too darn hard to
632 write a good client. In 1995, Ken Stevens decided to do something
635 Ken cast the problem as a data synchronization problem. Quote C_SYNC
636 RFC 5.1, section `Abstract':
638 This is a specification for a new method of synchronizing game data
639 in the Empire client with data in the server.
641 and section `Objectives':
643 This new mode of communication between the server and the client will
644 be called C_SYNC communication and will satisfy the following 6
647 (1) Output format will be version independent. So if someone is
648 using an old EmpireToolkit, then it will still work with a newer
649 version of the server.
651 (2) Every C_SYNC message will be a self-contained packet. i.e. the
652 client will not need to depend on previous messages (header messages)
653 to determine the meaning of a C_SYNC message.
655 (3) A C_SYNC message will be able to represent any of the
656 player-accessible data that is contained in the server database (e.g.
657 enemy ships, nations).
659 (4) Bandwidth will be minimized (i.e. the format will be as
660 concise as possible) while remaining human-readable (i.e. no
661 binary messages). [Note that data compression may be added at a later
662 date, but if it is added, it will be added on a separate port to
663 maintain backwards compatability.]
665 (5) The client will be able to tell the server whether it wants
666 to receive C_SYNC messages and whether these messages can be sent
667 asynchroniously (via "toggle sync" and "toggle async" respectively).
669 (6) A portable ANSI C EmpireToolkit will be made available for
670 parsing C_SYNC messages and managing the data they contain.
672 C_SYNC worked by hooking into ef_write() & friends so it could
673 `synchronize' the client on game state changes.
675 Sounds jolly good, doesn't it?
677 Well, it was a failure, and Wolfpack ripped it out right away. Quote
680 Changes to Empire 4.0.0 - Initial release
681 * Initial Wolfpack release - Long live the Wolfpack!!!!
683 * Removed C_SYNC. This is done for 2 reasons. 1) None of us like it or
684 wish to support it. 2) We envision a better scheme for doing similar
685 things will come along.
687 But *why* did it fail? Just because Steve McClure hated it? Nope.
688 C_SYNC failed for several different reasons, each of them bad, but
689 only the last one is truly fundamental.
691 a. Lack of a rigorous and complete definition. The RFC is long on
692 syntax, but short on semantics. For instance, the unit type was
693 encoded as a number. Unit characteristics happened to be dumped in
694 an order that matched these numbers, but that wasn't defined
697 b. Overly complicated syntax. Trouble with encoding of strings.
699 c. Buggy implementation. Malformed C_SYNC messages, duplicate
700 messages, missing messages, semantically incorrect messages, you
703 d. Change of crew before it was finished. Wolfpack took over and
704 understandable wasn't interested in this half-finished mess.
706 None of the above is a fundamental, inherent flaw of the idea. The
707 next one is more serious:
709 e. It failed to achieve objective (4), and therefore slowed down
710 clients too much to be of use in real-time combat. When you fired
711 from a bunch of ships, C_SYNC would push complete records for all
712 the ships and the target to you. Most of that data is redundant.
714 That's because C_SYNC didn't transmit state changes, it
715 resynchronized state, and the pieces of state it could transmit
718 The network was slower then. But let's not be complacent. I/O is
719 slow. Always was, most likely ever will be.
721 Maybe sending the messages out of band (separate TCP stream) would
724 And here comes the killer:
726 f. The data to sync is not readily available the server.
728 Yup. Think about it. The game state on the server is *not* the
729 same as on the client. The server grants the client a carefully
730 limited view on certain parts of server game state on certain
733 To be complete, a machine-readable protocol must disclose as much
734 information as the human-readable output. Tracking server game
735 state changes cannot do that alone. For instance, lookout tells
736 you ship#, owner and location. That event does not trigger any
737 state change on the server!
739 To be correct, a machine-readable protocol must disclose no more
740 information than the human-readable output. When you observe a
741 server game state change, you can only guess what event triggered
742 it, and what it disclosed to which player. You're stuck with
743 conservative assumptions. That's the death knell for completeness.
744 Correct assumptions will be non-obvious, so correctness is
745 non-obvious, too, hence hard to achieve and maintain.
747 Bottom line: tracking server state change cannot support a complete
748 client protocol for hard theoretical reasons, and I believe it
749 cannot support a correct one for practical reasons.
751 Oddly enough, people criticized C_SYNC for all the flaws it had (and
752 some it hadn't), except for f.
754 What now? Throw up our hands in despair and give up? Nah. Ken tried
755 a shortcut, and it didn't work. That doesn't mean there's no way at
756 all. I believe the only way to get this done right is by tracking
757 *events*. Whenever something is printed to a player, be it live
758 connection or telegram, we need to transmit precisely the same
759 information in machine-readable form. Much more work.
761 xdump shares valuable ideas with C_SYNC, e.g. using selector
762 meta-data. It is, however, much more modest in scope. We're pretty
763 sure we can get it right and get it done in a reasonable time frame.