Jump to content

Data structures

From Galactic Empire wiki

Galactic Empire uses C structures both as live state and as the bytes passed to Btrieve. A field's type, order, and offset can therefore be part of the saved-game format. The principal definitions are in GEMAIN.H; the 16-bit ge-next and 32-bit V10 ports must be read as distinct layouts, not as structures whose bytes can be copied between platforms.

Persistent records

The game has separate user, ship, planet-family, and (in standard ge-next) mail databases. The functions geudb(), gepdb(), and gesdb() select the corresponding Btrieve file and key. A Btrieve file is opened with the compiled sizeof its record structure, so adding a field without removing or adjusting filler is not merely a source-code change.

Record Database role Classic structure Classic Btrieve record 16-bit ge-next 0.3 structure / record Elwynor V10 structure / record ge-next32 structure / record
WARUSR One persistent player 255 bytes 246 bytes 246 bytes 256 bytes 256 bytes
WARSHP One persistent ship 514 bytes 501 bytes 501 bytes 512 bytes 512 bytes
GALSECT Sector index 512 bytes 493 bytes 493 bytes 512 bytes 512 bytes
GALPLNT Planet body 512 bytes 493 bytes 493 bytes 512 bytes 512 bytes
GALWORM Wormhole body 512 bytes 493 bytes 493 bytes 512 bytes 512 bytes

The shipped classic MBMGEUSR.NEW, MBMGESHP.NEW, and MBMGEPLT.NEW Btrieve templates declare fixed record lengths of 246, 501, and 493 bytes respectively. The corresponding classic .DAT files have the same lengths. These are the actual allotted logical record lengths, not the Btrieve page size or physical slot size. The planet database uses 493 bytes for each of its three record types.

Classic's GEMAIN.H uses filler expressions written around nominal 256-byte user and 512-byte ship/planet targets. Summing its members under the byte-packed 16-bit layout instead gives the classic structure sizes shown in the table: the user structure is 255 bytes, not 256, and the ship structure is 514, not 512. These are calculated structure sizes, distinct from the fixed record lengths read from the shipped Btrieve templates. Classic opens each Btrieve file with sizeof the compiled structure; the file's own fixed-length definition is a separate constraint. ge-next's 16-bit 0.3 structures use explicit filler sized to the real 246/501/493-byte records rather than perpetuating the misleading classic arithmetic.

The shipped Elwynor V10 and ge-next32 Btrieve files confirm 256-byte user records and 512-byte ship and planet-family records, matching their respective structures; ge-next32 also checks all five structure sizes at compile time. Equal lengths do not imply compatible layouts: Elwynor's port and ge-next32 arrange or use fields differently, so their database records should not be exchanged without conversion. A 16-bit record is not a truncated 32-bit record either: member order, alignment, and some compatibility slots differ. In particular, 0.3's reordered records are not compatible with pre-0.3 saved games; the 0.3 release notes require a reset.

WARUSR: player account

userid is the identity key. score is a roster-sorting key; teamcode is another indexed key. Standard ge-next also stores cash, ship counts and highest assigned ship number, total and player-only kills, planet count, separate kill/planet score components, population, 30 option bytes, and eight faction-relation bytes. Its score is the cumulative game score, while the kill and planet components describe where it came from. See Scoring and rosters and factions.

Classic reserved a 40-byte waste area and retained a debt field from an apparent earlier or unfinished credit system. The 2.0d release notes say the debt line was removed from REPORT ACCOUNT, and 3.2e still includes an unused message about a bank limiting cash advances because of outstanding debt. The field is initialized to zero, but the surviving 3.2e code does not appear to incur or repay player debt. Not every odd-looking field was dead: rospos was assigned during roster processing, displayed in reports, and used in kill-bonus calculation. ge-next eventually removed the unused space and the obsolete stored roster-position field, replacing its behavior with a roster lookup. A block of key_filler in the 16-bit 0.3 structure deliberately keeps teamcode at its required Btrieve key offset. A developer must not remove that block merely because it has no gameplay value.

In ge-arena, the same persistent user-record family retains account settings and stores an arena_wins[] count for each mode. Its score is repurposed as total arena wins for roster sorting; per-match kills, deaths, time, bases, and mode score live in ARENAPLAYER, not in permanent ship records. The win array takes bytes from reserved filler so that the arena variant keeps the same record length within its architecture. Arena and standard-game databases are separate and should not be interchanged.

WARSHP: ship state

A ship record begins with its owner ID and shipno, followed by name, class, status, coordinates, heading and target heading, speed and target speed, damage, flux energy, phaser and shield state, cloak state, and live control damage. It also holds the current lock, last attacker, ship kill counts, inventory, repair and combat timers, deployed decoy references, and incoming torpedo/missile tracking. The ltorps[] and lmissl[] arrays are ordnance pursuing this ship, not a list of everything this ship has fired. Later fields include upgrade bits, jamming, weapon throttles, overspeed accumulation, and state used by Cybertrons and other NPCs. Some fields are explicitly user-only or NPC-only even though they share the same record format.

The live ship slot or MajorBBS channel is not the same thing as persistent shipno. A slot can be reused; it must not be treated as a stable player identity in delayed combat state. See Ship slot and Ship number. Standard ge-next stores player and NPC ship records in the ship database; ge-arena uses disposable match ships, while its persistent user records remain. ge-arena's damage_notice byte for Cyber-Base warning thresholds consumes one byte of the 16-bit ship filler rather than increasing WARSHP.

Classic carried fields associated with abandoned or partial ideas, including train and an emulate flag in the original header. The 0.2.6 line still retained the old field ordering, a 35-character ship-name array, and several fields added into legacy spare space. Before RC2, the ship-name array was brought into line with its actual usable length. In 0.3 RC2, status, coord, movement values, combat state, equipment, and NPC state were regrouped, and unused/redundant members were removed. The new field order makes the record easier to audit but changes offsets, which is why old databases cannot be opened as though they were 0.3 records.

Sector, planet, and wormhole

GALSECT, GALPLNT, and GALWORM are different records in the same planet database. Each begins with xsect, ysect, plnum, and type, allowing a shared sector/number key. The sector index has plnum == 0 and contains numplan plus up to nine PLNTCOORD entries identifying the type and position of each body. Planets and wormholes use numbered body records; their filler makes every record in this Btrieve file the same length. PKEY is the small lookup key used to address these records, not another stored body format. See Sector and Wormholes.

A GALPLNT record holds its coordinates, owner, name, environment and resource ratings, cash and tax, tax rate, trade password, fourteen ITEM entries, beacon, spy owner, team code, and update bookkeeping. Each ITEM has quantity, production rate, allied-sales flag, reserve, allied markup, and amount sold to allies. An unowned planet still has a full planet record; ownership does not determine whether the record exists. Standard ge-next uses timestamp and plantimesave to schedule and resume production, and stores the nebula seed in Zygor's nebseed field. ge-arena leaves its nebula seed in memory; its planet filler instead contains shield/phaser boosts and flags for upgrades or maintenance, alongside the normal item fields. See Planetary production, Zygor, and Nebulas.

GALWORM stores entrance coordinates, a destination coordinate, visibility flag, and optional name. It has no planet economy or owner. Both planet and wormhole records share the sector key prefix and fixed-size database slot, but their remaining bytes mean different things. Blindly interpreting a wormhole as GALPLNT would turn destination and padding bytes into bogus planet fields.

Mail

Standard 16-bit ge-next opens its mail database using sizeof(MESSAGE) + GEMSGSIZ, documented as a 1000-byte packed record. MESSAGE is a MajorBBS-style message header and text envelope; MAIL and MAILSTAT are GE's payload formats placed inside that text, not independent 1000-byte Btrieve records. A normal delayed report can carry a template ID, class, recipient, names, coordinates, and numeric parameters; a production-status report uses MAILSTAT to carry a planet snapshot. The 32-bit port wraps the V10 message header and a fixed GE text payload in GEMESSAGE, and opens the database using sizeof(GEMESSAGE). ge-arena does not use the mail database. See Classic bug: Rejected mail leaks into output.

The published classic GE source includes message.h but does not provide the file. GE's private mail database uses a message structure modeled on the MajorBBS mail header; classic could also route reports to a player's MajorBBS mailbox. The classic release notes describe GE mail as the older path and say that a later release made the personal mailbox the default while retaining GE mail through USEGEMSG. The distributed 3.2e MBMGEMSG.MSG, however, sets USEGEMSG to YES, selecting GE mail. The notes do not establish why the GE database adopted this structure or when the two delivery paths first appeared. Early ge-next reconstructed MESSAGE.H from MajorBBS GME.H to restore the missing definition. It subsequently removed the MajorBBS-mail delivery option and kept GE mail, then folded the reconstructed MESSAGE definition directly into GEMAIN.H.

Teams

TEAM is a fixed-width team entry containing the code, name, member count, deletion date, password, and secret. Standard ge-next loads and saves its team table to MPOGETEA.DAT as a separate text-backed table, not as a Btrieve planet/user/ship record. ge-arena does not use that file; any future arena teams are intended to be match-local. User and planet teamcode fields still exist in their record layouts. See Teams.

Live tables and workspaces

Type Lifetime and purpose
COORD Two floating-point world coordinates. coord1() derives a sector; coord2() derives the within-sector position.
PLANETAB One ship/channel's cached nearby planets and wormholes. It is refreshed as sectors change; stale or cross-user cache use can produce incorrect gravity/orbit behavior. See Classic bug: Cross-user planet cache corruption.
SCANTAB / SHIPTAB Per-user scan contacts, assigned letters, distances, and headings. This is a bounded display/index table, not a persistent roster of all ships. See Scantab.
MINE Global active-mine slot: owner marker, timer, and location. Ownerless and unused are distinct markers in ge-next.
TORPEDO / MISSILE Compact incoming-projectile entries in WARSHP, including the firing ship slot, distance to impact, and for missiles remaining energy.
SHIP Loaded ship-class settings and NPC function hooks; configured from the ship-class MSG file, not a user ship record. ge-arena adds per-mode class and starting-loadout fields to this in-memory configuration.
S00 / WORMTAB Predefined neutral-zone body descriptions and temporary wormhole-pair construction data.
ENTRYTAB Per-channel throttling/timing state for ship-entry announcements.
ARENAPLAYER ge-arena-only live player state: lobby readiness, match state, selected ship, respawn delay, kills/deaths, and mode score. It is reset independently of persistent arena wins.

The game also uses globals such as warsptr, waruptr, plptr, and temporary record buffers as shared workspaces. These are intentional in the synchronous MajorBBS execution model; they are not independent copies for concurrent requests. A port that introduces concurrent handlers must isolate or serialize access to them.

How the layout evolved

  • Classic 3.2e: The large records contained reserved filler and some fields with little or abandoned gameplay use, such as WARUSR.waste, user/planet debt, and GALPLNT.technology. Others that look obscure, such as rospos, were active. Classic packed multiple planetary body types into a shared fixed-length database, and relied on compile-time structure sizes and key offsets.
  • ge-next 0.1: The first compiled ge-next release fixed cash/tax overflow behavior and several planetary and ship calculations while remaining broadly close to classic's storage scheme. These fixes should not be confused with 0.3's later wholesale layout change.
  • ge-next 0.2 and 0.2.6: The NPC and planet-update overhaul added faction relation bytes to WARUSR, repurposed ship state for new NPC behavior, and replaced the planet's unused debt field with update timing. Subsequent pre-0.3 work added separate user-kill counts, a nebula seed, lock and entry state, and upgrade/overspeed fields, largely by using space in the legacy-shaped records. The 0.2.6 header still shows classic-style ordering and reserved areas.
  • ge-next 0.3 RC2 onward: The broad restructuring removed obsolete fields, grouped related data, normalized types and names, assigned explicit offsets in comments, and recalculated filler. A separate 0.3 fix corrected GALPLNT's filler after its actual item-array size was verified. Compatibility with earlier saved games was deliberately broken; resetting the databases was required. Later arena builds consume reserved bytes for arena wins, powerups, and NPC notices without changing each build's fixed record length.

The source comments documenting offsets are an audit aid, not a substitute for checking sizeof and actual Btrieve key definitions. The V10 port uses fixed-width SHORT, USHORT, and ULONG fields where appropriate and compile-time size assertions for the major records. Copying a 16-bit .DAT file to the 32-bit port is not a record conversion; it requires a deliberate field-by-field migration, including key layout, signedness, and reserved bytes.

See also