UNPKG

@mixer/interactive-node

Version:

A NodeJS and Browser compatible client for mixer.com's interactive 2 Protocol

163 lines 6.11 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var events_1 = require("events"); var util_1 = require("./util"); var ClockSyncerState; (function (ClockSyncerState) { /** * Indicates that the clock syncer has JUST started up. */ ClockSyncerState[ClockSyncerState["Started"] = 0] = "Started"; /** * Indicates that the clock syncer is actively synchronizing its time with the server. */ ClockSyncerState[ClockSyncerState["Synchronizing"] = 1] = "Synchronizing"; /** * Indicates that the clock syncer is not actively synchronizing. */ ClockSyncerState[ClockSyncerState["Idle"] = 2] = "Idle"; /** * Indicates that the clock syncer has been stopped. */ ClockSyncerState[ClockSyncerState["Stopped"] = 3] = "Stopped"; })(ClockSyncerState = exports.ClockSyncerState || (exports.ClockSyncerState = {})); var defaultOptions = { checkInterval: 30 * 1000, sampleSize: 3, threshold: 1000, sampleDelay: 5000, }; /** * Clock syncer's goal is to keep a local clock in sync with a server clock. * * It does this by sampling the server time a few times and then monitoring the * local clock for any time disturbances. Should these occur it will re-sample the * server. * * After the sample period it is able to provide a delta value for its difference * from the server clock, which can be used to make time based adjustments to local * time based operations. */ var ClockSync = /** @class */ (function (_super) { __extends(ClockSync, _super); function ClockSync(options) { var _this = _super.call(this) || this; _this.state = ClockSyncerState.Stopped; _this.deltas = []; _this.cachedDelta = null; _this.options = Object.assign({}, defaultOptions, options); return _this; } /** * Starts the clock synchronizer. It will emit `delta` events, * when it is able to calculate the delta between the client and the server. */ ClockSync.prototype.start = function () { var _this = this; this.state = ClockSyncerState.Started; this.deltas = []; this.sync().then(function () { _this.expectedTime = Date.now() + _this.options.checkInterval; _this.checkTimer = setInterval(function () { return _this.checkClock(); }, _this.options.checkInterval); }); }; ClockSync.prototype.checkClock = function () { var now = Date.now(); var diff = Math.abs(now - this.expectedTime); if (diff > this.options.threshold && this.syncing === null) { this.sync(); } this.expectedTime = Date.now() + this.options.checkInterval; }; ClockSync.prototype.sync = function () { var _this = this; this.state = ClockSyncerState.Synchronizing; var samplePromises = []; for (var i = 0; i < this.options.sampleSize; i++) { samplePromises.push(util_1.delay(i * this.options.sampleDelay).then(function () { return _this.sample(); })); } this.syncing = Promise.all(samplePromises).then(function () { if (_this.state !== ClockSyncerState.Synchronizing) { return; } _this.state = ClockSyncerState.Idle; _this.emit('delta', _this.getDelta()); return undefined; }); return this.syncing.then(function () { return (_this.syncing = null); }); }; ClockSync.prototype.sample = function () { var _this = this; if (this.state === ClockSyncerState.Stopped) { return Promise.resolve(null); } var transmitTime = Date.now(); return this.options .sampleFunc() .then(function (serverTime) { return _this.processResponse(transmitTime, serverTime); }) .catch(function (err) { if (_this.state !== ClockSyncerState.Stopped) { return err; } }); }; /** * Halts the clock synchronizer. */ ClockSync.prototype.stop = function () { this.state = ClockSyncerState.Stopped; if (this.checkTimer) { clearInterval(this.checkTimer); } }; /** * Gets the current delta value from the synchronizer. */ ClockSync.prototype.getDelta = function (forceCalculation) { if (this.cachedDelta === null || forceCalculation) { this.cachedDelta = this.calculateDelta(); } return this.cachedDelta; }; ClockSync.prototype.calculateDelta = function () { if (this.deltas.length === 0) { return 0; } if (this.deltas.length === 1) { return this.deltas[0]; } var sorted = this.deltas.slice(0).sort(); var midPoint = Math.floor(sorted.length / 2); if (sorted.length % 2) { return sorted[midPoint]; } else { return (sorted[midPoint + 1] + sorted[midPoint]) / 2; } }; ClockSync.prototype.processResponse = function (transmitTime, serverTime) { var receiveTime = Date.now(); var rtt = receiveTime - transmitTime; var delta = serverTime - rtt / 2 - transmitTime; return this.addDelta(delta); }; ClockSync.prototype.addDelta = function (delta) { // Add new one this.deltas.push(delta); // Re-calculate delta with this number return this.getDelta(true); }; return ClockSync; }(events_1.EventEmitter)); exports.ClockSync = ClockSync; //# sourceMappingURL=ClockSync.js.map