UNPKG

sam-ecs

Version:

A specialized entity component system

274 lines (221 loc) 9.96 kB
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } //ClientManager.js// /** * @description - Exactly like the orignal Manager but has some helper * methods for clients * @author - Sam Faulkner */ //constants var MAXIMUM_BUFFER_SIZE = 12; var Manager = require('./Manager.js'); var StateManager = require('./StateManager.js'); var StateWorker; if (typeof window !== 'undefined' && window.Worker) StateWorker = require("worker!./ClientWorker.js"); var ClientManager = function (_Manager) { _inherits(ClientManager, _Manager); function ClientManager(socket) { _classCallCheck(this, ClientManager); var _this2 = _possibleConstructorReturn(this, (ClientManager.__proto__ || Object.getPrototypeOf(ClientManager)).call(this)); _this2._user = undefined; _this2._lastAcknowledgedState = -1; _this2._eventBuffer = new Array(); _this2._eventPending = false; _this2.eventFun = _this2.eventFun.bind(_this2); _this2.addEmitSideEffect(_this2.eventFun); _this2._socket = socket; _this2.setMaxBufferSize(MAXIMUM_BUFFER_SIZE); _this2._otherStateManager = new StateManager(MAXIMUM_BUFFER_SIZE); var _this = _this2; socket.on('UPDATE', function (data) { _this.receiveState(data); }); socket.on('CONNECTION', function () { socket.emit('BUFFER', MAXIMUM_BUFFER_SIZE - 4); socket.emit('ACKNOWLEDGE', { 'tick': _this2._lastAcknowledgedState }); }); return _this2; } /** * @description - Returns the socket object * @returns {Socket.io} the socket.io object */ _createClass(ClientManager, [{ key: 'getSocket', value: function getSocket() { return this._socket; } /** * @description - The event side affect which will buffer events before they are * sent to the server * @param {Object} event - the event object */ }, { key: 'eventFun', value: function eventFun(event) { var _this3 = this; if (!event.local) { Object.assign(event, { 'tick': this._currentTick }); this._eventBuffer.push(event); setTimeout(function () { _this3.sendNextEvent(); }, 0); } } /** * @description - Sets the user for this client manager * @param {String} value - the value to set the user to */ }, { key: 'setUser', value: function setUser(value) { this._user = value; } /** * @description - Accessor method for the server * @returns {String} the current user */ }, { key: 'getUser', value: function getUser() { return this._user; } /** * @description - Receiver function for state updates from the server * @param {Object} stateObject - the state object received from the server */ }, { key: 'receiveState', value: function receiveState(stateObject) { var tick = stateObject.tick; // we don't care about states in the past? if (tick < this._lastAcknowledgedState) { return; } if (_typeof('window') === undefined || !window.Worker) { if (this._lastAcknowledgedState > 0 && tick > this._lastAcknowledgedState) { this._stateManager.restoreState(tick); // this._otherStateManager.mergeState( // this._otherStateManager._serializeState( // this._stateManager.getBufferedState(this._lastAcknowledgedState).state // ), // this._componentManager // ); } // this._otherStateManager.mergeState(stateObject.state, this._componentManager); this._stateManager.mergeState(stateObject.state, this._componentManager); if (tick < this._currentTick) { this._actionManager.reApplyFrom(tick, this._stateManager); } // we're behind (cheating?) else { this._currentTick = tick; this._stateManager.bufferState(this._currentTick); } this._stateManager.mergeEntireState(this._otherStateManager.getState(), this._componentManager); var previousAckState = this._lastAcknowledgedState; this._lastAcknowledgedState = tick; this._socket.emit('ACKNOWLEDGE', { 'tick': tick }); // we've acknowledged a new state. Time to send a new event if (tick > previousAckState) { this._eventPending = false; this.sendNextEvent(); } } else { if (!this._worker) { this._worker = new StateWorker(); var _this = this; this._worker.onmessage = this.workerResolve.bind(this); } var oldDict = this._stateManager.getBufferedState(tick); var oldState; if (oldDict !== undefined) { var oldStateObject = oldDict.state; oldState = this._stateManager._serializeState(oldStateObject); } else { oldState = this._stateManager.serializeState(); } this._otherStateManager.mergeEntireState(this._stateManager.getState(), this._componentManager); this._otherStateManager.mergeState(oldState, this._componentManager); this._worker.postMessage({ 'oldState': oldState, 'deltaState': stateObject.state, 'tick': tick, 'receivedTick': this._currentTick }); //we're behind if (tick > this._currentTick) { this._currentTick = tick; this._stateManager.bufferState(this._currentTick); } } } /** * @description - Message handler for communicating with the web worker * @param {Object} mes - the message returned from the web worker */ }, { key: 'workerResolve', value: function workerResolve(mes) { var data = mes.data; var tick = data.tick; data = data.state; var receivedTick = data.receivedTick; this._otherStateManager.mergeState(data, this._componentManager); if (tick < receivedTick) { this._actionManager.reApplyFrom(tick, this._otherStateManager); } // we're behind (cheating?) else if (tick > receivedTick) { console.warn("We're behind the server by: " + (tick - receivedTick) + " ticks."); // this._currentTick = tick; // this._stateManager.bufferState(this._currentTick); } this._stateManager.mergeState(this._otherStateManager._serializeState(this._otherStateManager.getDeltaState(this._stateManager.getSubState())), this._componentManager); var previousAckState = this._lastAcknowledgedState; this._lastAcknowledgedState = tick; this._socket.emit('ACKNOWLEDGE', { 'tick': tick }); // we've acknowledged a new state. Time to send a new event if (tick > previousAckState) { this._eventPending = false; this.sendNextEvent(); } } /** * @description - Sends the next event in the event buffer to the server */ }, { key: 'sendNextEvent', value: function sendNextEvent() { if (!this._eventPending) { var event = this._eventBuffer.shift(); if (event) { this._eventPending = true; this._socket.emit('EVENT', event); } } } /** * @description - Returns the currently queued events waiting to be sent to the server * @returns {Array} the array of currently queued events */ }, { key: 'getQueuedEvents', value: function getQueuedEvents() { return this._eventBuffer; } }, { key: 'update', value: function update() { _get(ClientManager.prototype.__proto__ || Object.getPrototypeOf(ClientManager.prototype), 'update', this).call(this); this._currentTick++; } }]); return ClientManager; }(Manager); module.exports = ClientManager;