Sort events upon creation, and keep ordered (#411)
* Sort events at start, as otherwise we risk misidentifying the last event * Keep inserted events in the correct order, ensuring we don't misidentify the last event - e.g. network conditions mean that 'live' events come in non-sequentially - or so that adding custom events to an existing event works * Ensure we maintain original ordering while inserting a new event which has an identical timestamp to an existing event. This came up with a series of mutations which had the same timestamp but needed to be applied in the correct order * Fast track the common case of a new event being added which occurs after all prior events
This commit is contained in:
@@ -129,6 +129,8 @@ export class Replayer {
|
|||||||
events: Array<eventWithTime | string>,
|
events: Array<eventWithTime | string>,
|
||||||
config?: Partial<playerConfig>,
|
config?: Partial<playerConfig>,
|
||||||
) {
|
) {
|
||||||
|
events.sort((a1, a2) => a1.timestamp - a2.timestamp);
|
||||||
|
|
||||||
if (!config?.liveMode && events.length < 2) {
|
if (!config?.liveMode && events.length < 2) {
|
||||||
throw new Error('Replayer need at least 2 events.');
|
throw new Error('Replayer need at least 2 events.');
|
||||||
}
|
}
|
||||||
@@ -520,6 +522,8 @@ export class Replayer {
|
|||||||
castFn();
|
castFn();
|
||||||
}
|
}
|
||||||
this.service.send({ type: 'CAST_EVENT', payload: { event } });
|
this.service.send({ type: 'CAST_EVENT', payload: { event } });
|
||||||
|
|
||||||
|
// events are kept sorted by timestamp, check if this is the last event
|
||||||
if (
|
if (
|
||||||
event ===
|
event ===
|
||||||
this.service.state.context.events[
|
this.service.state.context.events[
|
||||||
|
|||||||
@@ -237,7 +237,29 @@ export function createPlayerService(
|
|||||||
if (machineEvent.type === 'ADD_EVENT') {
|
if (machineEvent.type === 'ADD_EVENT') {
|
||||||
const { event } = machineEvent.payload;
|
const { event } = machineEvent.payload;
|
||||||
addDelay(event, baselineTime);
|
addDelay(event, baselineTime);
|
||||||
events.push(event);
|
|
||||||
|
let end = events.length - 1;
|
||||||
|
if (events[end].timestamp <= event.timestamp) {
|
||||||
|
// fast track
|
||||||
|
events.push(event);
|
||||||
|
} else {
|
||||||
|
let insertion_index = -1;
|
||||||
|
let start = 0;
|
||||||
|
while (start <= end) {
|
||||||
|
let mid = Math.floor((start + end) / 2);
|
||||||
|
if (events[mid].timestamp <= event.timestamp) {
|
||||||
|
start = mid + 1;
|
||||||
|
} else {
|
||||||
|
end = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (insertion_index === -1) {
|
||||||
|
insertion_index = start;
|
||||||
|
}
|
||||||
|
events.splice(insertion_index, 0, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const isSync = event.timestamp < baselineTime;
|
const isSync = event.timestamp < baselineTime;
|
||||||
const castFn = getCastFn(event, isSync);
|
const castFn = getCastFn(event, isSync);
|
||||||
if (isSync) {
|
if (isSync) {
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export class Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
this.actions.sort((a1, a2) => a1.delay - a2.delay);
|
|
||||||
this.timeOffset = 0;
|
this.timeOffset = 0;
|
||||||
let lastTimestamp = performance.now();
|
let lastTimestamp = performance.now();
|
||||||
const { actions } = this;
|
const { actions } = this;
|
||||||
|
|||||||
Reference in New Issue
Block a user