Jump to content

Porting Galactic Empire

From Galactic Empire wiki

Porting Galactic Empire means reproducing a MajorBBS game, not merely getting its C source to compile on another platform. GE depends on host callbacks, shared workspaces, Btrieve records and keys, and the order in which timed events run. A port should decide which classic behavior it is preserving and which known defects it is correcting.

Choose a compatibility target

There is a difference between reproducing the intentions of classic GE, reproducing every visible behavior of the 3.2e binary, and reproducing the source's accidental behavior. Those goals can conflict: widening an overflowing calculation may be the right fix for gameplay but will not reproduce a classic exploit. State the target and record intentional departures; the Classic bug database supplies concrete cases.

When comparing versions, separate original GE behavior from ge-next's changes. Elwynor's 32-bit port is useful for checking host and architecture differences, but it is not a substitute for the 16-bit source or a running classic game. See Versions of Galactic Empire.

Recreate the host contract

GE is entered through MajorBBS module registration. The host supplies the current channel and account, invokes input and lifecycle callbacks, runs scheduled rtkick() callbacks, and provides message formatting and Btrieve services. A replacement host needs to preserve the effects of those services, not just their function names. See MajorBBS integration and Game execution flow.

Distinguish ordinary exit, carrier loss, account deletion, GE's midnight maintenance, and the BBS disconnecting users for its own cleanup. These follow different paths and can have different consequences for a ship. ge-next's use of rsmodes during a cleanup-related hangup is an intentional correction to classic behavior.

Keep session and world state separate

The 16-bit code uses usrnum to select a live BBS channel and global pointers and scratch variables for the current operation. A channel is reusable, not a permanent account or ship identifier. A concurrent port must isolate or serialize operations that touch this shared state, including scans, output, database buffers, and delayed ordnance. Merely copying the globals into a server-wide object would introduce cross-user races.

GE has several lifetimes that must remain distinct: the connection, saved user record, current ship, created sector, and weapons already in flight. Ship destruction, leaving the module, hanging up, and deleting an account are not interchangeable cleanup operations. A projectile can outlive the connection that fired it; represent a departed owner explicitly. See Ship slot, Scantab, and Data structures.

Preserve event order

Movement, automatic ship systems, projectiles, NPC decisions, and planetary updates have different schedules. Replacing them with one undifferentiated loop can change which hit receives kill credit, when a damaged ship stops acting, whether a weapon intercepts before its target moves, and when a planet is updated. Preserve cadence and meaningful ordering before changing mechanics. See Game timing and Game execution flow.

Sectors are generated on demand, with planet and wormhole records stored in Btrieve. A concurrent implementation must prevent two operations from independently creating the same sector or conflicting destination records. Test both first discovery and later reads of an existing sector. See Sector and Wormholes.

Match storage and arithmetic deliberately

Classic GE assumes a 16-bit int, 32-bit long, narrow byte fields, and fixed Btrieve key layouts. Wider native arithmetic, different packing, or different signedness can change play and saved data. Classic structures do not all have the same size as their Btrieve records, so sizeof cannot be accepted uncritically. The 32-bit ports use explicit narrow types and different record lengths. See Integer and storage assumptions and Data structures.

Btrieve has behavior beyond reading and writing bytes: key ordering, duplicate and nonmodifiable keys, record-position operations, return codes, and file-selection state can all affect GE. Preserve the observable behavior of the operations GE calls. MajorBBS message formatting and output-buffer clearing are likewise part of the interface; a message delivered to the wrong channel is a portability bug even if its text is correct.

A practical verification sequence

  1. Establish a baseline with stock classic files, configuration, and more than one connected player. Record command output and timed behavior before changing the implementation.
  2. Audit persistent structures against the actual Btrieve templates and key definitions. Add size and offset checks where the target compiler permits them.
  3. Reproduce module entry, exit, hangup, cleanup, message output, and database operations before tuning combat or NPC logic.
  4. Test arithmetic boundaries: zero, negative input, maximum inventories, signed/unsigned scores, coordinate axes, galaxy edges, and numeric sentinels.
  5. Test delayed behavior: moving targets, wormholes, mines, projectiles whose owner leaves, and users reconnecting on a reused channel.
  6. Compare a confirmed defect against the Classic bug database, then apply any correction separately from unrelated porting changes.
  7. Where an emulator and real MajorBBS disagree, verify host behavior before adding a game-side workaround.

See also