Allow rewinding a replay without desyncing it

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-11 21:14:19 -07:00
parent 410af788fc
commit 0f0c7437a6
2 changed files with 27 additions and 20 deletions

View File

@ -25,13 +25,20 @@ class CC2Demo {
this.blob_seed = this.bytes[2];
}
*[Symbol.iterator]() {
decompress() {
let duration = 0;
let l = this.bytes.length;
if (l % 2 === 0) {
l--;
}
let input = new Set;
for (let p = 3; p < l; p += 2) {
duration += this.bytes[p];
}
let inputs = new Uint8Array(duration);
let i = 0;
let t = 0;
let input = 0;
for (let p = 3; p < l; p += 2) {
// The first byte measures how long the /previous/ input remains
// valid, so yield that first. Note that this is measured in 60Hz
@ -44,24 +51,31 @@ class CC2Demo {
t += delay;
while (t >= 3) {
t -= 3;
yield input;
inputs[i] = input;
i++;
}
let input_mask = this.bytes[p + 1];
let is_player_2 = ((input_mask & 0x80) !== 0);
input = this.bytes[p + 1];
let is_player_2 = ((input & 0x80) !== 0);
// TODO handle player 2
if (is_player_2)
continue;
}
for (let [action, bit] of Object.entries(CC2_DEMO_INPUT_MASK)) {
if ((input_mask & bit) === 0) {
input.delete(action);
// TODO maybe turn this into, like, a type, or something
return {
inputs: inputs,
length: duration,
get(t) {
if (t >= this.inputs.length) {
return new Set;
}
else {
input.add(action);
let input = this.inputs[t];
return new Set(Object.entries(CC2_DEMO_INPUT_MASK).filter(([action, bit]) => input & bit).map(([action, bit]) => action));
}
}
}
},
};
}
}
@ -1826,7 +1840,6 @@ const MAX_SIMULTANEOUS_REQUESTS = 5;
resolve(game);
}
console.log(game);
return promise;
}

View File

@ -898,7 +898,7 @@ class Player extends PrimaryView {
play_demo() {
this.restart_level();
let demo = this.level.stored_level.demo;
this.demo_faucet = demo[Symbol.iterator]();
this.demo_faucet = demo.decompress();
this.level.force_floor_direction = demo.initial_force_floor_direction;
this.level._blob_modifier = demo.blob_seed;
// FIXME should probably start playback on first real input
@ -908,13 +908,7 @@ class Player extends PrimaryView {
get_input() {
let input;
if (this.demo_faucet) {
let step = this.demo_faucet.next();
if (step.done) {
input = new Set;
}
else {
input = step.value;
}
input = this.demo_faucet.get(this.level.tic_counter);
}
else {
// Convert input keys to actions. This is only done now