diff --git a/client/garageserver.io.js b/client/garageserver.io.js index d2a0dbd..0c99d35 100644 --- a/client/garageserver.io.js +++ b/client/garageserver.io.js @@ -10,7 +10,7 @@ options = { onEvent(callback(data)), onWorldState(callback(state)), onPing(callback(pingDelay)), - onUpdateClientPredictionReady(callback(playerId, playerCurrentState, inputs, deltaTime)), + onUpdateClientPredictionReady(callback(playerId, playerCurrentState, entityCurrentStates, inputs, deltaTime)), onInterpolation(callback(previousState, targetState, amount) : newState), onReady(callback), logging: true @@ -88,10 +88,12 @@ var GarageServerIO = (function (socketio) { this.time = time; } - function Entity(id, maxUpdateBuffer) { + function Entity(id, referrerId, referrerSeq, maxUpdateBuffer) { this.updates = []; this.maxUpdateBuffer = maxUpdateBuffer; this.id = id; + this.referrerId = referrerId; + this.referrerSeq = referrerSeq; this.state = {}; this.inputController = new InputController(); } @@ -142,7 +144,7 @@ var GarageServerIO = (function (socketio) { }; function Player(id, maxUpdateBuffer) { - Entity.call(this, id, maxUpdateBuffer); + Entity.call(this, id, null, null, maxUpdateBuffer); } Player.prototype = Object.create(Entity.prototype); @@ -151,8 +153,9 @@ var GarageServerIO = (function (socketio) { this.maxUpdateBuffer = maxUpdateBuffer; } EntityController.prototype = { - add: function (id) { - var entity = new Entity(id, this.maxUpdateBuffer); + add: function (id, referrerId) { + var referrerSeq = this.entities.filter(function (value) { return value.referrerId === referrerId; }).length; + var entity = new Entity(id, referrerId, referrerSeq, this.maxUpdateBuffer); this.entities.push(entity); return entity; }, @@ -303,9 +306,16 @@ var GarageServerIO = (function (socketio) { addInput = function (clientInput) { _playerController.entities.some(function (player) { if (player.id === _stateController.id) { - if (_stateController.clientSidePrediction && _options.onUpdatePlayerPrediction) { + if (_stateController.clientSidePrediction && _options.onUpdateClientPredictionReady) { + var entityCurrentStates = []; + _entityController.entities.forEach(function(entity) { + if (entity.referrerId === player.id) { + entityCurrentStates.push({ id: entity.id, state: entity.state }); + } + }); + player.inputController.add(clientInput); - _options.onUpdateClientPredictionReady(player.state, [{ input: clientInput }], _stateController.physicsDelta); + _options.onUpdateClientPredictionReady(player.id, player.state, entityCurrentStates, [{ input: clientInput }], _stateController.physicsDelta); } _socket.emit('i', [ clientInput, player.inputController.sequenceNumber, _stateController.renderTime ]); } @@ -358,11 +368,16 @@ var GarageServerIO = (function (socketio) { }, addEntity = function (id, state) { - + _entityController.add(id, _stateController.id).state = state; }, updateEntityState = function (id, state) { - + _entityController.entities.some(function(entity) { + if (entity.id === id) { + entity.state = state; + return true; + } + }); }, removeEntity = function (id) { diff --git a/documentation/ClientAPI.md b/documentation/ClientAPI.md index 25e1120..b0bc7ba 100644 --- a/documentation/ClientAPI.md +++ b/documentation/ClientAPI.md @@ -113,7 +113,7 @@ The current ping in milliseconds. --- ```js -options.onUpdateClientPredictionReady(callback(playerId, playerCurrentState, inputs, deltaTime)) +options.onUpdateClientPredictionReady(callback(playerId, playerCurrentState, entityCurrentStates: [, {id, state}], inputs, deltaTime)) ``` If using client side prediction, this callback will fire when you should update player and entity states, based on the current states for player and entities, inputs to be processed, and the delta time. @@ -126,6 +126,9 @@ The id of the player. `playerCurrentState` **object literal** The current state of the player. +`entityCurrentStates` **array** +The list of all entities and their current state that were invoked by the player. + `inputs` **array** List of all the inputs to be processed. diff --git a/documentation/QuickStart.md b/documentation/QuickStart.md index cca9dad..f336454 100644 --- a/documentation/QuickStart.md +++ b/documentation/QuickStart.md @@ -52,7 +52,7 @@ GarageServerIO.initializeGarageServer('http://insertmygameserverurlhere.com', { onReady: function () { // Call your game loop }, - onUpdateClientPredictionReady: function (playerId, playerCurrentState, inputs, deltaTime) { + onUpdateClientPredictionReady: function (playerId, playerCurrentState, entityCurrentStates, inputs, deltaTime) { var newState = {}; if (!playerCurrentState.x) { playerCurrentState.x = 0; diff --git a/documentation/ServerAPI.md b/documentation/ServerAPI.md index 578f8ab..f2c082b 100644 --- a/documentation/ServerAPI.md +++ b/documentation/ServerAPI.md @@ -202,13 +202,15 @@ New state of the entity containing all of the properties specific to an entity f #### addEntity --- ```js -GarageServerIO.addEntity(id) +GarageServerIO.addEntity(id, referrerId) ``` Notify GarageServer.IO that a new entity has been added to the game. `id` **string** Id of the entity to be added. +`referrerId` **string** +Id of the player who invoked/create the entity - optional and used primarily for client side prediction. #### removeEntity --- ```js diff --git a/example/game.js b/example/game.js index acd27db..751ec35 100644 --- a/example/game.js +++ b/example/game.js @@ -29,7 +29,7 @@ Game.prototype.update = function () { self = this; players.forEach(function (player) { - var newState = gamePhysics.getNewPlayerState(player.state, player.inputs, self.physicsDelta, self.server); + var newState = gamePhysics.getNewPlayerState(player.id, player.state, player.inputs, self.physicsDelta, self.server); self.server.updatePlayerState(player.id, newState); }); diff --git a/example/public/javascripts/game.js b/example/public/javascripts/game.js index 3cbe541..9a0b96f 100644 --- a/example/public/javascripts/game.js +++ b/example/public/javascripts/game.js @@ -10,8 +10,8 @@ $(function () { GarageServerIO.initializeGarageServer('', { logging: true, onReady: startGame, - onUpdateClientPredictionReady: function(playerId, playerCurrentState, inputs, deltaTime) { - GarageServerIO.updatePlayerState(playerId, GamePhysics.getNewPlayerState(playerCurrentState, inputs, deltaTime)); + onUpdateClientPredictionReady: function(playerId, playerCurrentState, entityCurrentStates, inputs, deltaTime) { + GarageServerIO.updatePlayerState(playerId, GamePhysics.getNewPlayerState(playerId, playerCurrentState, inputs, deltaTime)); }, onInterpolation: GamePhysics.getInterpolatedState }); diff --git a/example/shared/core.js b/example/shared/core.js index 527c822..4e9a46f 100644 --- a/example/shared/core.js +++ b/example/shared/core.js @@ -1,6 +1,6 @@ (function(exports){ - exports.getNewPlayerState = function (state, inputs, deltaTime, garageServer) { + exports.getNewPlayerState = function (id, state, inputs, deltaTime, garageServer) { var i = 0, distance = 0, newState = {}; if (!state.ang && state.ang !== 0) { @@ -28,7 +28,7 @@ } else if (inputs[i].input === 'space') { if (garageServer && (new Date().getTime() - newState.lastFire) > 1000) { var newId = guid(); - garageServer.addEntity(newId); + garageServer.addEntity(newId, id); garageServer.updateEntityState(newId, { x: newState.x, y: newState.y, ang: newState.ang } ); newState.lastFire = new Date().getTime(); } diff --git a/lib/controllers/entitycontroller.js b/lib/controllers/entitycontroller.js index fa09987..84c49e9 100644 --- a/lib/controllers/entitycontroller.js +++ b/lib/controllers/entitycontroller.js @@ -8,7 +8,7 @@ function EntityController (maxHistorySecondBuffer) { } EntityController.prototype = { - add: function (id) { + add: function (id, referrerId) { var newEntity, entityFound = false; this.entities.some(function (entity) { @@ -20,7 +20,8 @@ EntityController.prototype = { }); if (!entityFound) { - newEntity = new entity(id, this.maxHistorySecondBuffer); + var referrerSeq = this.entities.filter(function (value) { return value.referrerId === referrerId; }).length; + newEntity = new entity(id, referrerId, referrerSeq, this.maxHistorySecondBuffer); this.entities.push(newEntity); } return newEntity; diff --git a/lib/entities/entity.js b/lib/entities/entity.js index b57f88b..b9391cc 100644 --- a/lib/entities/entity.js +++ b/lib/entities/entity.js @@ -2,10 +2,12 @@ var history = require('./history'); exports = module.exports = Entity; -function Entity (id, maxHistorySecondBuffer) { +function Entity (id, referrerId, referrerSeq, maxHistorySecondBuffer) { this.state = {}; this.sequence = 1; this.id = id; + this.referrerId = referrerId; + this.referrerSeq = referrerSeq; this.maxHistorySecondBuffer = maxHistorySecondBuffer; this.stateHistory = []; this.region = ''; diff --git a/lib/entities/player.js b/lib/entities/player.js index 7b6032c..bda36f9 100644 --- a/lib/entities/player.js +++ b/lib/entities/player.js @@ -3,7 +3,7 @@ var entity = require('./entity'); exports = module.exports = Player; function Player (socket, maxHistorySecondBuffer) { - entity.call(this, socket.id, maxHistorySecondBuffer); + entity.call(this, socket.id, null, null, maxHistorySecondBuffer); this.socket = socket; this.inputs = []; } diff --git a/lib/garageserver.io.js b/lib/garageserver.io.js index 6e49dfb..f79bed5 100644 --- a/lib/garageserver.io.js +++ b/lib/garageserver.io.js @@ -150,8 +150,8 @@ GarageServer.prototype.updateEntityState = function (id, state) { this.game.updateEntityState(id, state); }; -GarageServer.prototype.addEntity = function (id) { - this.game.addEntity(id); +GarageServer.prototype.addEntity = function (id, referrerId) { + this.game.addEntity(id, referrerId); }; GarageServer.prototype.removeEntity = function (id) { diff --git a/lib/garageservergame.js b/lib/garageservergame.js index 1e53c5f..79681a7 100644 --- a/lib/garageservergame.js +++ b/lib/garageservergame.js @@ -103,8 +103,8 @@ GarageServerGame.prototype.removePlayer = function (id) { this.playerController.remove(id); }; -GarageServerGame.prototype.addEntity = function (id) { - this.entityController.add(id); +GarageServerGame.prototype.addEntity = function (id, referrerId) { + this.entityController.add(id, referrerId); }; GarageServerGame.prototype.removeEntity = function (id) {