From f28dee8927afaf3677523c27022d480c5719f55a Mon Sep 17 00:00:00 2001 From: Jeremiah Billmann Date: Wed, 20 May 2015 04:04:31 +0000 Subject: [PATCH] Furthered GarageServer.IO API calls to support client side prediction for entities - #20 - still have to change how client side prediction is invoked/processed. --- client/garageserver.io.js | 65 +++++++++++++++++++----------- example/public/javascripts/game.js | 7 +++- example/shared/core.js | 11 +++-- lib/garageserver.io.js | 9 ++++- lib/garageservergame.js | 31 ++++++++++++-- 5 files changed, 89 insertions(+), 34 deletions(-) diff --git a/client/garageserver.io.js b/client/garageserver.io.js index 0c99d35..5898b22 100644 --- a/client/garageserver.io.js +++ b/client/garageserver.io.js @@ -275,13 +275,23 @@ var GarageServerIO = (function (socketio) { } }); - _socket.on('re', function (id) { - removeEntity(id); + _socket.on('re', function (data) { + if (_stateController.clientSidePrediction) { + _entityController.entities.forEach(function (entity) { + if (entity.referrerId === data.id && entity.referrerSeq === data.seq) { + removeEntity(entity.id); + } + }); + } + else { + removeEntity(data.id); + } + if (_options.logging) { - console.log('garageserver.io:: socket removeEntity ' + id); + console.log('garageserver.io:: socket removeEntity ' + data.id); } if (_options.onEntityRemove) { - _options.onEntityRemove(id); + _options.onEntityRemove(data.id); } }); @@ -367,8 +377,8 @@ var GarageServerIO = (function (socketio) { _playerController.remove(id); }, - addEntity = function (id, state) { - _entityController.add(id, _stateController.id).state = state; + addEntity = function (id) { + _entityController.add(id, _stateController.id); }, updateEntityState = function (id, state) { @@ -393,7 +403,19 @@ var GarageServerIO = (function (socketio) { updatePlayers = function (data) { data.ps.forEach(function (playerState) { - updateEntity(_playerController, playerState, data.t); + var playerFound = false; + _playerController.entities.some(function (player) { + if (player.id === playerState[0]) { + playerFound = true; + player.updateState(playerState[1], playerState[2], data.t); + return true; + } + }); + + if (!playerFound) { + var newPlayer = _playerController.add(playerState[0]); + newPlayer.addUpdate(playerState[1], playerState[2], data.t); + } if (_options.onPlayerUpdate) { _options.onPlayerUpdate(playerState[1]); @@ -403,7 +425,19 @@ var GarageServerIO = (function (socketio) { updateEntities = function (data) { data.es.forEach(function (entityState) { - updateEntity(_entityController, entityState, data.t); + var entityFound = false; + _entityController.entities.some(function (entity) { + if (entity.id === entityState[0] || (entity.referrerId === entityState[3] && entity.referrerSeq === entityState[4])) { + entityFound = true; + entity.updateState(entityState[1], entityState[2], data.t); + return true; + } + }); + + if (!entityFound) { + var newEntity = _entityController.add(entityState[0]); + newEntity.addUpdate(entityState[1], entityState[2], data.t); + } if (_options.onEntityUpdate) { _options.onEntityUpdate(entityState[1]); @@ -411,21 +445,6 @@ var GarageServerIO = (function (socketio) { }); }, - updateEntity = function (entityController, entityState, time) { - var entityFound = false; - entityController.entities.some(function (entity) { - if (entity.id === entityState[0]) { - entityFound = true; - entity.updateState(entityState[1], entityState[2], time); - return true; - } - }); - if (!entityFound) { - var newEntity = entityController.add(entityState[0]); - newEntity.addUpdate(entityState[1], entityState[2], time); - } - }, - processEntityStatesCurrent = function (entityController) { entityController.entities.forEach(function (entity) { if (entity.anyUpdates() && !entity.inputController.any()) { diff --git a/example/public/javascripts/game.js b/example/public/javascripts/game.js index 9a0b96f..77a8118 100644 --- a/example/public/javascripts/game.js +++ b/example/public/javascripts/game.js @@ -10,8 +10,11 @@ $(function () { GarageServerIO.initializeGarageServer('', { logging: true, onReady: startGame, - onUpdateClientPredictionReady: function(playerId, playerCurrentState, entityCurrentStates, inputs, deltaTime) { - GarageServerIO.updatePlayerState(playerId, GamePhysics.getNewPlayerState(playerId, playerCurrentState, inputs, deltaTime)); + onUpdateClientPredictionReady: function (playerId, playerCurrentState, entityCurrentStates, inputs, deltaTime) { + entityCurrentStates.forEach(function (entity) { + GarageServerIO.updateEntityState(entity.id, GamePhysics.getNewEntityState(entity.state, deltaTime)); + }); + GarageServerIO.updatePlayerState(playerId, GamePhysics.getNewPlayerState(playerId, playerCurrentState, inputs, deltaTime, GarageServerIO)); }, onInterpolation: GamePhysics.getInterpolatedState }); diff --git a/example/shared/core.js b/example/shared/core.js index 4e9a46f..9cc76aa 100644 --- a/example/shared/core.js +++ b/example/shared/core.js @@ -26,7 +26,7 @@ } else if (inputs[i].input === 'up') { distance += (125 * deltaTime); } else if (inputs[i].input === 'space') { - if (garageServer && (new Date().getTime() - newState.lastFire) > 1000) { + if ((new Date().getTime() - newState.lastFire) > 1000) { var newId = guid(); garageServer.addEntity(newId, id); garageServer.updateEntityState(newId, { x: newState.x, y: newState.y, ang: newState.ang } ); @@ -50,12 +50,15 @@ } exports.getNewEntityState = function (state, deltaTime) { + var newState = {}; var distance = 300 * deltaTime; var newPoint = getPoint(state.ang, distance, state.x, state.y); - state.x = newPoint.x; - state.y = newPoint.y; + + newState.ang = state.ang; + newState.x = newPoint.x; + newState.y = newPoint.y; - return state; + return newState; }; exports.getInterpolatedState = function (previousState, targetState, amount) { diff --git a/lib/garageserver.io.js b/lib/garageserver.io.js index f79bed5..b814807 100644 --- a/lib/garageserver.io.js +++ b/lib/garageserver.io.js @@ -36,6 +36,7 @@ function GarageServer(socketio, options) { this.socketPath = namespace; this.io = socketio; + this.clientSidePrediction = options.clientSidePrediction; this.registerSocketEvents(options); this.game = new garageServerGame(options, function (state, region) { if (!region) { @@ -155,7 +156,13 @@ GarageServer.prototype.addEntity = function (id, referrerId) { }; GarageServer.prototype.removeEntity = function (id) { - this.io.of(this.socketPath).emit('re', id); + if (this.clientSidePrediction) { + var entity = this.game.getEntity(id); + this.io.of(this.socketPath).emit('re', { id: entity.referrerId, seq: entity.referrerSeq }); + } + else { + this.io.of(this.socketPath).emit('re', { id: id }); + } this.game.removeEntity(id); }; diff --git a/lib/garageservergame.js b/lib/garageservergame.js index 79681a7..8626ad8 100644 --- a/lib/garageservergame.js +++ b/lib/garageservergame.js @@ -43,18 +43,32 @@ GarageServerGame.prototype.broadcastState = function () { }; GarageServerGame.prototype.getState = function (controller) { - var states = []; + var states = [], + clientSidePrediction = this.options.clientSidePrediction; + controller.entities.forEach(function (entity) { - states.push([ entity.id, entity.state, entity.sequence ]); + if (clientSidePrediction && entity.referrerId != null) { + states.push([ entity.id, entity.state, entity.sequence, entity.referrerId, entity.referrerSeq ]); + } + else { + states.push([ entity.id, entity.state, entity.sequence ]); + } }); return states; }; GarageServerGame.prototype.getStateByRegion = function (controller, region) { - var states = []; + var states = [], + clientSidePrediction = this.options.clientSidePrediction; + controller.entities.forEach(function (entity) { if (entity.region === region) { - states.push([ entity.id, entity.state, entity.sequence ]); + if (clientSidePrediction && entity.referrerId != null) { + states.push([ entity.id, entity.state, entity.sequence, entity.referrerId, entity.referrerSeq ]); + } + else { + states.push([ entity.id, entity.state, entity.sequence ]); + } } }); return states; @@ -107,6 +121,15 @@ GarageServerGame.prototype.addEntity = function (id, referrerId) { this.entityController.add(id, referrerId); }; +GarageServerGame.prototype.getEntity = function (id) { + for (var idx = 0; idx < this.entityController.entities.length; idx ++) { + if (this.entityController.entities[idx].id === id) { + return this.entityController.entities[idx]; + } + } +}; + + GarageServerGame.prototype.removeEntity = function (id) { this.entityController.remove(id); };