Monday, August 6, 2012

Little summary

Now, after two months of work, it is time to show progress and describe the architecture of events recorder. This is a class diagramm, which can give some understanding of how does events recorder relates with other classes of scummvm.




Lets describe it particulary by means of subsistems, which it records. 
User Input: to record events from mouse and keyboard, eventRecorder implements DefaultEventMapper. During the initialisation, it registers itself as a mapper. So, every event occurring in system, is catched with EventRecorder's pollEvent function. This function checks state of the recorder and calls PlaybackFile's writeEvent() function to write this event in temporary memory buffer. If the buffer is full, playback file dumps it to file and takes screenshot, calculates checksumm and dumps it to the file too.
To playback events, recorder does everything in reverse order. It implements EventSource and reguster in EventDispatcher as the source of events. Every time when game engine calls pollEvent function, EventDispatcher calls pollEvent function of EventRecorder. This function checks if the EventRecorder in playback mode and calls getNextEvent function of PlaybackFile class. This function read event from events memory buffer, and if buffer is empty, it reads next events chunk from the file. Also it reads screenshot from the file and compare it with current screen image. If there are any differences, it shows warning message. 
System time:   To correct playback process we need to write and to play system time when a game engines queried for it. To do this, we inserted processMillis call to OSystem_SDL::getMillis function. This function in recording mode writes system time to the file and in playback mode reads time from the file and give it to  a game. The problem is that this meathod registers time queries not only from a game engine, but from GUI and TimerManager. It ruins playback, cause gui and timer manager not synchronised with engine logic. To solve this problem, two improvements to the code were made. The first one -  parameter which disallow to register this query was added to getMillis function. By default, this parameter sets to false. It sets to true in calls from GUI. The second one - call timers update routine from EventRecorder/ became possible.
Audio: As audio fragments are played in other thread and controlled only with SDL subsystem, there could be some problems with  audio flow synchronization and user events. For instance, in Kyrandia, when you click anywhere while somebody's talking, sound interrupts and mouse event doesn't pass to the game. In the same situation, durring playback, sound may be played a little bit faster. And mouse's event will be passed to the game and it's possible that the whole gameplay will chrash. To avoid this unpleasant situation, new class had been added: NullSdlMixerManager. It updates with EventsRecorder and "output" sound to nowhere. And we may always know that during playback sound ends the same time it ends during recording.
Random numbers: as an engine use pseudo random sequences, and we can prognose the whole sequence by initial value of random numbers generator, it's very easy to record and playback random events. It's necessary only to hold this initial value and restore it. Every time RandomSource is created, it calls getRandomSeed() function of EventRecorder. If the recorder not in recording or playback mode, this function works just as bypass.In playback mode it changes initial value to recorded one.