sam-ecs
Version:
A specialized entity component system
382 lines (325 loc) • 11.9 kB
JavaScript
'use strict';
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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
//ActionManager.js//
/**
* @description - Manages the current actions in the ECS manager,
* along with reducers that act on those actions
* @author - Sam Faulkner
*/
//node imports
var Dict = require('collections/dict.js');
var SortedArray = require('collections/sorted-array.js');
//constants
var MAXIMUM_BUFFER_LENGTH = 8;
var ActionManager = function () {
function ActionManager(bufferSize) {
_classCallCheck(this, ActionManager);
this._actionQueues = new Array();
this._actionQueues.push(new Array());
this._actionQueues.push(new Array());
this._actionQueueIndex = 0;
this._reducers = new Dict();
this._actionBuffer = new SortedArray([], function (first, second) {
return first.tick == second.tick;
}, function (first, second) {
return first.tick - second.tick;
});
this._maxBufferSize = bufferSize || MAXIMUM_BUFFER_LENGTH;
}
_createClass(ActionManager, [{
key: 'setMaxBufferSize',
value: function setMaxBufferSize(value) {
this._maxBufferSize = value;
}
/**
* @description - Adds a reducer that will reduce actions of
* the given type
* @param {Function} reducer - the function that will be called with
* the given action
* @param {Array} actionTypes - the types of action that this reducer
* will be called with
*/
}, {
key: 'addReducer',
value: function addReducer(reducer, actionTypes) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = actionTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var actionType = _step.value;
if (!this._reducers.has(actionType)) {
this._reducers.set(actionType, new Array());
}
this._reducers.get(actionType).push(reducer);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
/**
* @description - Removes the reducer from the given action types
* @param {Function} reducer - the function to remove
* @param {Array} actionTypes - the types of actions to remove it from
*/
}, {
key: 'removeReducer',
value: function removeReducer(reducer, actionTypes) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = actionTypes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var actionType = _step2.value;
//should I throw an error here?
if (!this._reducers.has(actionType)) {
continue;
}
var reducerArray = this._reducers.get(actionType);
if (!reducerArray.includes(reducer)) {
throw new TypeError("Given reducer can't be removed from type: " + actionType);
}
reducerArray.splice(reducerArray.indexOf(reducer), 1);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
/**
* @description - dispatches all queued actions to
* the respective reducers
* @param {StateManager} stateManager - the state manager
* that is passed to reducers
*/
}, {
key: 'update',
value: function update(stateManager, currentTick) {
var activeActionQueue = this._actionQueues[this._actionQueueIndex];
this._actionQueueIndex += 1;
this._actionQueueIndex %= this._actionQueues.length;
this.bufferActions(activeActionQueue, currentTick);
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = activeActionQueue[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var action = _step3.value;
this.dispatchNow(action, stateManager);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
activeActionQueue.length = 0;
}
/**
* @description - Calls the reducers associated with
* the given action. Does NOT do any error checking
* if the action is valid
* @param {Object} action - the action to be dispatched
*/
}, {
key: 'dispatchNow',
value: function dispatchNow(action, stateManager) {
var reducerArray = this.getReducers(action.type);
if (reducerArray) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = reducerArray[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var reducer = _step4.value;
reducer(action, stateManager, this);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
}
/**
* @description - Reapplies all the actions from the given tick
* @param {Number} tick - the tick to re apply the actions from in time
* @param {StateManager} stateManager - the stateManager to pass to the reducers
*/
}, {
key: 'reApplyFrom',
value: function reApplyFrom(tick, stateManager) {
var actionBuffer = this.getActionBuffer(tick);
if (!actionBuffer) {
actionBuffer = this._actionBuffer.min();
if (!actionBuffer) throw new RangeError("Tick: '" + tick.toString() + "' is out of range!");
actionBuffer = actionBuffer.actions;
}
while (actionBuffer) {
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = actionBuffer[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var action = _step5.value;
this.dispatchNow(action, stateManager);
}
//increase the tick until we get to the newest in the buffer
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
actionBuffer = this.getActionBuffer(++tick);
}
}
/**
* @description - Takes the given action queue and adds it to the
* array of buffered actions
* @param {Array} actionQueue - queue of actions to buffer
* @param {Number} currentTick - the current tick to store with the
* queue to keep track of the actions
*/
}, {
key: 'bufferActions',
value: function bufferActions(actionQueue, currentTick) {
while (this._actionBuffer.length >= this._maxBufferSize) {
this._actionBuffer.shift();
}
var replicatedActionQueue = [];
actionQueue.forEach(function (value, index, array) {
replicatedActionQueue.push(Object.assign({}, value));
});
this._actionBuffer.push({
'tick': currentTick,
'actions': replicatedActionQueue
});
}
/**
* @description - Returns the actions dispatched within the given tick
* @param {Number} tick - the tick to retrieve the actions from
* @returns {Array} the buffered action array
*/
}, {
key: 'getActionBuffer',
value: function getActionBuffer(tick) {
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = this._actionBuffer.toArray()[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var buffer = _step6.value;
if (buffer.tick == tick) {
return buffer.actions;
}
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
}
/**
* @description - Returns the entire array of action buffers
* @returns {Array} the entire buffer of action arrays
*/
}, {
key: '_getActionBuffer',
value: function _getActionBuffer() {
return this._actionBuffer;
}
/**
* @description - Returns the array of reducers associated with
* the given action type
* @param {String} actionType - the type of action to return the reducers for
*/
}, {
key: 'getReducers',
value: function getReducers(actionType) {
if (this._reducers.has(actionType)) {
return this._reducers.get(actionType);
}
}
/**
* @description - Queues an action to be sent out
* next update
* @param {Object} action - the action to be queued
*/
}, {
key: 'dispatch',
value: function dispatch(action) {
if (!action || !action.type) {
throw new TypeError("Action must be defined and have a type!");
}
this._actionQueues[this._actionQueueIndex].push(action);
}
/**
* @description - Returns the currently queued actions
* @returns {Array} the list of actions currently queued
*/
}, {
key: 'getQueuedActions',
value: function getQueuedActions() {
return this._actionQueues[this._actionQueueIndex];
}
}]);
return ActionManager;
}();
module.exports = ActionManager;