Plan for input recording and playback

(Redirected from Ohrkey)
Jump to navigation Jump to search

The input recording file format is a simple stream of binary codes. I was tempted to use a RELOAD file, but decided against it. Input recording and playback is always be one way linear progression through a stream, so it doesn't make sense to have a random-access structured file, especially with whatever overhead that involves. I still might have tried it, but allmodex does not already depend on reload, and I didn't want to add that dependency just for this.

Also, whenever we change the format and that breaks playback of old input files, that is no disaster, since input recordings are fragile by nature and will break for a ton of other reasons too.

I know this format isn't super efficient in terms of storage space, but that isn't what I care about. I am focusing on implementation simplicity and clarity.

.ohrkey file format[edit]

byte size data type description
12 string "OHRRPGCEkeys" header
4 int32 version code, currently 4. Playback should refuse files if this this indicates a format that won't be understood.
8 double random number generator seed used for this recording session
? tickblocks a stream of 0 or more variable-sized tickblocks


byte size data type description
4 int32 tick number. Ticks will always be in order, and ticks where no keys changed will be skipped.
1 byte milliseconds in the previous tick (setkeys-setkeys interval) (capped at 255ms). This is needed for determining key-repeat events.
1 byte number of keys stored in this tickblock
? keyblocks a stream of 0 or more keyblocks, of the number specified above.
1 byte number of characters of text input during this tick
? string text input during this tick, Latin-1 encoding (one byte per character). Number of characters specified above.


A keyblock is only stored for each keybd() element that has changed since the last tick

byte size data type description
1 byte key index
1 byte keybd value (never bigger than 7)

Proposal for Version 5 changes[edit]

This proposal is for adding a 'command stream' to the file format, which can be used both to store results of script commands which are nondeterministic, as well being able to store other arbitrary data (of arbitrary type) in the file without having to modify (and bloat) the file format. Every recorded value is prefixed with the ID of the script command that generated it, both as a sanity check and to allow producing meaningful dumps.

For example, the "sound is playing" script command could be implemented like this:

CASE 236'--sound is playing
 IF play_input ANDALSO replay_cmdstream_int(236, scriptret) THEN
  'nothing to do
  DIM sfxid as integer = backcompat_sound_id(retvals(0))
  IF sfxid >= 0 AND sfxid <= gen(genMaxSFX) THEN
   scriptret = sfxisplaying(sfxid)
  IF recinput THEN record_cmdstream_int 236, scriptret

We could also consider switching from 4 byte ints to RELOAD variable length integers, to save two bytes per tickblock for the first 7 minutes of the recording.


As above, but appended with:

byte size data type description
1 byte Number of cmdblocks
? cmdblocks Variable number; each as below


byte size data type description
2 bit * 13 (signed) (Least significant bits) Command id. Positive values refer to script commands, negative values contain other things. Used to check that the data being read is from the expected source and of the expected type, though this is not strictly required.
bit * 3 (Most significant bits) Type of data following:

0: integer, encoded using RELOAD VLI scheme
1: double
others: reserved (will probably include strings and typed script objects in future)

? ? As encoded above

For example, one likely candidate for a negative command ID is

cmd id data type description
-1 double TIMER. Written and read by a function wrapping use of FB's TIMER function for use in places where it affects the state of the game, eg playtimer

Possible additional features[edit]

  • hotkeys for Record/playback of macros.
  • should game/custom automatically record keys internally for including with crash reports? (what about passwords in custom?)
  • TAS features
    • Automatically switch from playback to record mode on the same file when a key is pressed.
    • Playback at max speed, but switch to normal speed X seconds before the end of the ohrkey file
  • Record mouse input
  • After Plan for improved joystick support is finished, joystick input should be recorded automatically, with no extra work.

Known determinism problems[edit]

  • Mouse and joystick input
  • Script commands like "system seconds", "seconds of play" and "milliseconds"
    • We could write a wrapper for the TIMER command that dumps the timer into the ohrkey stream when recording, and fetches the timer from the stream when playing back.
  • Audio functions like "sound is playing"
  • Global and function-local STATIC variables which cause differences if multiple games are played without quitting Game:
    • random_formation
    • targ and spread in menu_attack_targ_picker?
  • Loading from save slots