sam-ecs
Version:
A specialized entity component system
274 lines (221 loc) • 9.96 kB
JavaScript
'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;