UNPKG

lance-gg

Version:

A Node.js based real-time multiplayer game server

112 lines (98 loc) 3.35 kB
import EventEmitter from 'event-emitter'; const SIXTY_PER_SEC = 1000 / 60; const LOOP_SLOW_THRESH = 0.3; const LOOP_SLOW_COUNT = 10; type SchedulerOptions = { tick: () => void, period: number, delay: number } /** * Scheduler class * */ class Scheduler { private options: SchedulerOptions; private nextExecTime: number; private requestedDelay: number; private delayCounter: number; public emit: (event: string, arg?: any) => void; public on: (event: string, handler: any) => void; public once: (event: string, handler: any) => void; /** * schedule a function to be called * * @param {Object} options the options * @param {Function} options.tick the function to be called * @param {Number} options.period number of milliseconds between each invocation, not including the function's execution time * @param {Number} options.delay number of milliseconds to add when delaying or hurrying the execution */ constructor(options: SchedulerOptions) { this.options = Object.assign({ tick: null, period: SIXTY_PER_SEC, delay: SIXTY_PER_SEC / 3 }, options); this.nextExecTime = 0; this.requestedDelay = 0; this.delayCounter = 0; // mixin for EventEmitter let eventEmitter = EventEmitter(); this.on = eventEmitter.on; this.once = eventEmitter.once; this.emit = eventEmitter.emit; } // in same cases, setTimeout is ignored by the browser, // this is known to happen during the first 100ms of a touch event // on android chrome. Double-check the game loop using requestAnimationFrame nextTickChecker() { let currentTime = (new Date()).getTime(); if (currentTime > this.nextExecTime) { this.delayCounter++; this.callTick(); this.nextExecTime = currentTime + this.options.period; } window.requestAnimationFrame(this.nextTickChecker.bind(this)); } nextTick() { let stepStartTime = (new Date()).getTime(); if (stepStartTime > this.nextExecTime + this.options.period * LOOP_SLOW_THRESH) { this.delayCounter++; } else this.delayCounter = 0; this.callTick(); this.nextExecTime = stepStartTime + this.options.period + this.requestedDelay; this.requestedDelay = 0; setTimeout(this.nextTick.bind(this), this.nextExecTime - (new Date()).getTime()); } callTick() { if (this.delayCounter >= LOOP_SLOW_COUNT) { this.emit('loopRunningSlow'); this.delayCounter = 0; } this.options.tick(); } /** * start the schedule * @return {Scheduler} returns this scheduler instance */ start() { setTimeout(this.nextTick.bind(this)); if (typeof window === 'object' && typeof window.requestAnimationFrame === 'function') window.requestAnimationFrame(this.nextTickChecker.bind(this)); return this; } /** * delay next execution */ delayTick() { this.requestedDelay += this.options.delay; } /** * hurry the next execution */ hurryTick() { this.requestedDelay -= this.options.delay; } } export { Scheduler, SchedulerOptions }