2022-02-21 15:35:52 +00:00
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
2022-09-01 15:57:33 +00:00
|
|
|
exports.StateRecorderImpl = void 0;
|
2022-02-21 15:35:52 +00:00
|
|
|
const emu_1 = require("./emu");
|
|
|
|
class StateRecorderImpl {
|
|
|
|
constructor(platform) {
|
|
|
|
this.checkpointInterval = 10;
|
|
|
|
this.maxCheckpoints = 300;
|
|
|
|
this.reset();
|
|
|
|
this.platform = platform;
|
|
|
|
}
|
|
|
|
reset() {
|
|
|
|
this.checkpoints = [];
|
|
|
|
this.framerecs = [];
|
|
|
|
this.frameCount = 0;
|
|
|
|
this.lastSeekFrame = 0;
|
|
|
|
this.lastSeekStep = 0;
|
|
|
|
this.lastStepCount = 0;
|
|
|
|
if (this.callbackStateChanged)
|
|
|
|
this.callbackStateChanged();
|
|
|
|
}
|
|
|
|
frameRequested() {
|
|
|
|
var controls = {
|
|
|
|
controls: this.platform.saveControlsState(),
|
|
|
|
seed: (0, emu_1.getNoiseSeed)()
|
|
|
|
};
|
|
|
|
var requested = false;
|
|
|
|
// are we replaying? then we don't need to save a frame, just replace controls
|
|
|
|
if (this.lastSeekFrame < this.frameCount) {
|
|
|
|
this.loadControls(this.lastSeekFrame);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// record the control state, if available
|
|
|
|
if (this.platform.saveControlsState) {
|
|
|
|
this.framerecs.push(controls);
|
|
|
|
}
|
|
|
|
// time to save next frame?
|
|
|
|
requested = (this.frameCount++ % this.checkpointInterval) == 0;
|
|
|
|
}
|
|
|
|
this.lastSeekFrame++;
|
|
|
|
this.lastSeekStep = 0;
|
|
|
|
if (this.callbackStateChanged)
|
|
|
|
this.callbackStateChanged();
|
|
|
|
return requested;
|
|
|
|
}
|
|
|
|
numFrames() {
|
|
|
|
return this.frameCount;
|
|
|
|
}
|
|
|
|
currentFrame() {
|
|
|
|
return this.lastSeekFrame;
|
|
|
|
}
|
|
|
|
currentStep() {
|
|
|
|
return this.lastSeekStep;
|
|
|
|
}
|
|
|
|
recordFrame(state) {
|
|
|
|
this.checkpoints.push(state);
|
|
|
|
if (this.callbackNewCheckpoint)
|
|
|
|
this.callbackNewCheckpoint(state);
|
|
|
|
// checkpoints full?
|
|
|
|
if (this.checkpoints.length > this.maxCheckpoints) {
|
|
|
|
this.checkpoints.shift(); // remove 1st checkpoint
|
|
|
|
this.framerecs = this.framerecs.slice(this.checkpointInterval);
|
|
|
|
this.lastSeekFrame -= this.checkpointInterval;
|
|
|
|
this.frameCount -= this.checkpointInterval;
|
|
|
|
if (this.callbackStateChanged)
|
|
|
|
this.callbackStateChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
getStateAtOrBefore(frame) {
|
|
|
|
// initial frame?
|
|
|
|
if (frame <= 0 && this.checkpoints.length > 0)
|
|
|
|
return { frame: 0, state: this.checkpoints[0] };
|
|
|
|
var bufidx = Math.floor(frame / this.checkpointInterval);
|
|
|
|
var foundidx = bufidx < this.checkpoints.length ? bufidx : this.checkpoints.length - 1;
|
|
|
|
var foundframe = foundidx * this.checkpointInterval;
|
|
|
|
return { frame: foundframe, state: this.checkpoints[foundidx] };
|
|
|
|
}
|
|
|
|
loadFrame(seekframe, seekstep) {
|
|
|
|
seekframe |= 0;
|
|
|
|
seekstep |= 0;
|
|
|
|
if (seekframe == this.lastSeekFrame && seekstep == this.lastSeekStep) {
|
|
|
|
return seekframe; // already set to this frame
|
|
|
|
}
|
|
|
|
// TODO: what if < 1?
|
|
|
|
let { frame, state } = this.getStateAtOrBefore(seekframe - 1);
|
|
|
|
if (state) {
|
|
|
|
var numSteps = 0;
|
|
|
|
this.platform.pause();
|
|
|
|
this.platform.loadState(state);
|
|
|
|
// seek to frame index
|
|
|
|
while (frame < seekframe) {
|
|
|
|
if (frame < this.framerecs.length) {
|
|
|
|
this.loadControls(frame);
|
|
|
|
}
|
|
|
|
frame++;
|
|
|
|
numSteps = this.platform.advance(frame < seekframe); // TODO: infinite loop?
|
|
|
|
}
|
|
|
|
// TODO: if first frame, we must figure out # of steps
|
|
|
|
if (frame == 0) {
|
|
|
|
numSteps = this.platform.advance(true);
|
|
|
|
this.platform.loadState(state);
|
|
|
|
}
|
|
|
|
// seek to step index
|
|
|
|
// TODO: what if advance() returns clocks, but steps use insns?
|
|
|
|
if (seekstep > 0 && this.platform.advanceFrameClock) {
|
|
|
|
seekstep = this.platform.advanceFrameClock(null, seekstep);
|
|
|
|
}
|
|
|
|
// record new values
|
|
|
|
this.lastSeekFrame = seekframe;
|
|
|
|
this.lastSeekStep = seekstep;
|
|
|
|
this.lastStepCount = numSteps;
|
|
|
|
return seekframe;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loadControls(frame) {
|
|
|
|
if (this.platform.loadControlsState)
|
|
|
|
this.platform.loadControlsState(this.framerecs[frame].controls);
|
|
|
|
(0, emu_1.setNoiseSeed)(this.framerecs[frame].seed);
|
|
|
|
}
|
|
|
|
getLastCheckpoint() {
|
|
|
|
return this.checkpoints.length && this.checkpoints[this.checkpoints.length - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exports.StateRecorderImpl = StateRecorderImpl;
|
|
|
|
//# sourceMappingURL=recorder.js.map
|