I tried to make something workable in time, but it was much harder than expected. So I’ll show what I had done for this moment. Sources you can see at this LINK. There I’ll tell how and what I did to realize this idea.
I’ve read and criticism of my idea to link events with engine’s loop. I’ve desided that it’s realy hard in realization and impossible to make universal for all existing engines. Then I’ve desided to write every “external” event (with some exceptions), namely mouse’s moves and clicks, keyboard’s events, calling of delay and getmillis functions.
I’ve quite quickly writtem it, fortunately most of all this was already in existing code. Then I’ve tested it on SKY engine and everything worked perfect.
I needed to consolidate success and I’ve tested gameplay recording on KYRA engine. And there began problems. Events have become different from record very fast and gameplay absolutely different with from recorded. I’ve begun to find out what’s wrong. First of all, I’ve noticed, then getMillisec and delayMillisec functions called not only by engine. ScummVM (GUI system and something also) call them too. It’s giving undefined behavior, cause we don’t know what state have scummvm when we began record or replay. So, I’ve removed global hook of these functions and created method getMillis for EventRecorder class. Engine must call this method if want’s to record time event. I’ve changed all callings of g_system->getMillis to g_eventRec.getMillis. It’s become better.
Then I’ve noticed dependence of gameplay from audio playback. Following the tested game logic, if user presses mouse button during the speach, speach finishes and mouse event doesn’t passes to game. As sound is playing in separate thread – audio playback speed doesn’t corresponds to gameplay speed. So I’ve thought how it’s possible to record and playback of audio events and synchronize them with gameplay thread. During the code studying, I’ve noticed then substitution of arguments of mixCallback function in MixerImpl class affects to audio playback speed. I’ve inserted my hook code into this function. So audio event’s syncronizing with EventRecorder’s timer and occurs in same moments of time.
Unfortunatelly, this didn’t solve the problem of incorrect gameplay playback. So, for make debugging easier, I’ve visualised it. I’ve inserted debug output to log for every place where event occurs. Now when I’m recording gameplay, I’m saving log file and comparing it with playback’s log file using the diff utility. It allows to easyly detects places where playback events became different from recorded events.
Using this method I’ve found that in sound_midi.cpp function g_eventRect.getMillis called by timer handler, which is absolutely independend from engine and is a part of scummvm. It called randomly and make errors during the playback. For this time I’ve made calling of system getMillis and discrepancy has gone.
Then I’ve seen that played back gameplay is absolutely identically to the recorded before the sound begin plays. Since audio playback events begin to diverge. It happens becouse game logic and audio playback executing in different threads. It can be executed random number of gamecode while sound is playing. It makes undefined behavior. I’ve tryed to syncronize engine thread with audio thread. Now engine wait for finishing of audio playing and only then continues work.
Now I have problem related to wrong returned time of samples was played by certain chanal. I tryed to solve it in current commit, but think I’ll havn’t time before choosing of participants. And I had to make this post to show current progress of work.