From b1e4e7a52e1982437242b8cfff05f879ae551b56 Mon Sep 17 00:00:00 2001 From: Jeremiah Billmann Date: Wed, 3 Jul 2013 17:03:52 -0400 Subject: [PATCH] Fixes for #9 #10 and #11 --- client/garageserver.io.js | 105 ++++++++++++++++++----------- example/app.js | 2 +- example/public/javascripts/game.js | 51 ++++++-------- example/shared/core.js | 2 +- example/views/index.jade | 1 - lib/server/garageserver.io.js | 12 ++-- lib/server/garageservergame.js | 8 +-- 7 files changed, 97 insertions(+), 84 deletions(-) diff --git a/client/garageserver.io.js b/client/garageserver.io.js index 03e9988..b686e0a 100644 --- a/client/garageserver.io.js +++ b/client/garageserver.io.js @@ -5,10 +5,11 @@ options = { onPlayerReconnect: function (), onPlayerUpdate: function (state), onPlayerRemove: function (id), - onGameState: function (), + onGameState: function (state), onPing: function (pingDelay), onUpdatePlayerPhysics: function (state, inputs, deltaTime), - onInterpolation: function(currentState, previousState, targetState, amount) + onUpdate: function (), + onInterpolation: function(previousState, targetState, amount) logging: true, clientSidePrediction: true, interpolation: true, @@ -24,17 +25,26 @@ window.GarageServerIO = (function (window, socketio) { this.clientTime; this.renderTime; this.physicsDelta; + this.physicsIntervalId; + this.currentTime; + this.accumulator = 0.0; this.playerId; this.pingDelay = 100; this.interpolationDelay = 100; - this.fps = 0; - this.fpsLastUpdate = (new Date()) * 1 - 1; - this.fpsFilter = 50; } StateController.prototype = { setTime: function (serverTime) { this.clientTime = serverTime; this.renderTime = this.clientTime - this.interpolationDelay; + }, + accumulate: function () { + var newTime = new Date().getTime(), + frameTime = newTime - this.currentTime; + if (frameTime > 250) { + frameTime = 250; + } + this.currentTime = newTime; + this.accumulator += frameTime; } }; @@ -74,13 +84,18 @@ window.GarageServerIO = (function (window, socketio) { function Player(id) { this.updates = []; this.id = id; + this.currentState = {}; } Player.prototype = { anyUpdates: function () { return this.updates.length > 0; }, addUpate: function (state, seq, time) { - this.updates.push(new Update(state, seq, time)); + var newUpdate = new Update(state, seq, time); + if (this.updates.length === 0) { + this.currentState = newUpdate.state; + } + this.updates.push(newUpdate); if (this.updates.length > 120) { this.updates.splice(0, 1); } @@ -88,17 +103,17 @@ window.GarageServerIO = (function (window, socketio) { getLatestUpdate: function () { return this.updates[this.updates.length - 1]; }, - processState: function (playerState, time) { + processState: function (state, seq, time) { var updateFound = false; this.updates.some(function (update) { - if (update.seq === playerState.seq) { - update.state = playerState.state; + if (update.seq === seq) { + update.state = state; updateFound = true; return true; } }); if (!updateFound) { - this.addUpate(playerState.state, playerState.seq, time); + this.addUpate(state, seq, time); } }, getSurroundingPositions: function (time) { @@ -141,7 +156,7 @@ window.GarageServerIO = (function (window, socketio) { _inputController = new InputController(), _playerController = new PlayerController(), - connectToGarageServer = function (path, opts) { + initializeGarageServer = function (path, opts) { _options = opts; _socket = _io.connect(path + '/garageserver.io'); registerSocketEvents(); @@ -161,7 +176,7 @@ window.GarageServerIO = (function (window, socketio) { }); _socket.on('state', function(data) { if (_options.onGameState) { - _options.onGameState(); + _options.onGameState(data); } _stateController.physicsDelta = data.physicsDelta; }); @@ -195,6 +210,9 @@ window.GarageServerIO = (function (window, socketio) { }); _socket.on('removePlayer', function(id) { removePlayer(id); + if (_options.onPlayerRemove) { + _options.onPlayerRemove(id); + } if (_options.logging) { console.log('garageserver.io:: socket removePlayer ' + id); } @@ -211,6 +229,12 @@ window.GarageServerIO = (function (window, socketio) { }, interval); }, + start = function () { + var self = this; + _stateController.currentTime = new Date().getTime(); + _stateController.physicsIntervalId = setInterval(function () { self.update(); }, this.physicsDelta * 1000); + }, + getPlayerId = function () { return _stateController.playerId; }, @@ -221,9 +245,16 @@ window.GarageServerIO = (function (window, socketio) { removePlayer = function (id) { _playerController.removePlayer(id); + }, - if (_options.onPlayerRemove) { - _options.onPlayerRemove(id); + update = function () { + if (_options.onUpdate) { + _stateController.accumulate(); + while (_stateController.accumulator >= (_stateController.physicsDelta * 1000)) + { + _options.onUpdate(); + _stateController.accumulator -= (_stateController.physicsDelta * 1000); + } } }, @@ -232,7 +263,7 @@ window.GarageServerIO = (function (window, socketio) { if (_options.clientSidePrediction && _options.onUpdatePlayerPhysics) { _stateController.state = _options.onUpdatePlayerPhysics(_stateController.state, [{ input: clientInput }], _stateController.physicsDelta); } - _socket.emit('input', { input: clientInput, seq: _inputController.sequenceNumber, time: _stateController.renderTime }); + _socket.emit('input', [ clientInput, _inputController.sequenceNumber, _stateController.renderTime ]); }, updateState = function (data) { @@ -244,21 +275,21 @@ window.GarageServerIO = (function (window, socketio) { updatePlayersState = function (data) { data.playerStates.forEach(function (playerState) { - if (_socket.socket.sessionid === playerState.id) { + if (_socket.socket.sessionid === playerState[0]) { updatePlayerState(playerState); } else { updateOtherPlayersState(playerState, data.time); } if (_options.onPlayerUpdate) { - _options.onPlayerUpdate(playerState.state); + _options.onPlayerUpdate(playerState[1]); } }); }, updatePlayerState = function (playerState) { - _stateController.state = playerState.state; - _inputController.removeUpToSequence(playerState.seq); + _stateController.state = playerState[1]; + _inputController.removeUpToSequence(playerState[2]); if (_options.clientSidePrediction && _inputController.any()) { _stateController.state = _options.onUpdatePlayerPhysics(_stateController.state, _inputController.inputs, _stateController.physicsDelta); @@ -268,15 +299,15 @@ window.GarageServerIO = (function (window, socketio) { updateOtherPlayersState = function (playerState, time) { var playerFound = false; _playerController.players.some(function (player) { - if (player.id === playerState.id) { + if (player.id === playerState[0]) { playerFound = true; - player.processState(playerState, time); + player.processState(playerState[1], playerState[2], time); return true; } }); if (!playerFound) { - var newPlayer = _playerController.addPlayer(playerState.id); - newPlayer.addUpate(playerState.state, playerState.seq, time); + var newPlayer = _playerController.addPlayer(playerState[0]); + newPlayer.addUpate(playerState[1], playerState[2], time); } }, @@ -301,19 +332,20 @@ window.GarageServerIO = (function (window, socketio) { } }); }, - + getPlayerStatesInterpolated = function (stateCallback) { - var latestUpdate, positions, amount; + var positions, amount, newState; _playerController.players.forEach(function (player) { if (player.anyUpdates()) { - latestUpdate = player.getLatestUpdate(); positions = player.getSurroundingPositions(_stateController.renderTime); if (positions.previous && positions.target) { amount = getInterpolatedAmount(positions.previous.time, positions.target.time); - stateCallback(_options.onInterpolation(latestUpdate.state, positions.previous.state, positions.target.state, amount)); + newState = _options.onInterpolation(positions.previous.state, positions.target.state, amount); + player.currentState = newState = _options.onInterpolation(player.currentState, newState, _stateController.physicsDelta * 20); + stateCallback(player.currentState); } else { - stateCallback(latestUpdate.state); + stateCallback(player.currentState); } } }); @@ -325,25 +357,16 @@ window.GarageServerIO = (function (window, socketio) { amount = parseFloat((difference / range).toFixed(3)); return amount; - }, - - getFPS = function () { - var now, - thisFrameFPS = 1000 / ((now = new Date()) - _stateController.fpsLastUpdate); - - _stateController.fps += (thisFrameFPS - _stateController.fps) / _stateController.fpsFilter; - _stateController.fpsLastUpdate = now; - - return Math.round(_stateController.fps); }; return { - connectToGarageServer: connectToGarageServer, + initializeGarageServer: initializeGarageServer, + start: start, + update: update, addPlayerInput: addPlayerInput, getPlayerStates: getPlayerStates, getPlayerId: getPlayerId, - setPlayerState: setPlayerState, - getFPS: getFPS + setPlayerState: setPlayerState }; }) (window, io); \ No newline at end of file diff --git a/example/app.js b/example/app.js index 3c810f2..70fcc60 100644 --- a/example/app.js +++ b/example/app.js @@ -47,5 +47,5 @@ sockets.set('log level', 0); garageServer.createGarageServer(sockets,{ logging: true, - onUpdatePlayerPhysics: gamePhysics.OnProcessGamePhysics + onUpdatePlayerPhysics: gamePhysics.onUpdatePlayerPhysics }); diff --git a/example/public/javascripts/game.js b/example/public/javascripts/game.js index 8fc044c..46c6846 100644 --- a/example/public/javascripts/game.js +++ b/example/public/javascripts/game.js @@ -1,26 +1,21 @@ $(function () { - GarageServerIO.connectToGarageServer('http://garageserver_io.jbillmann.c9.io', { + var canvas = document.getElementById('gameCanvas'), ctxCanvas = canvas.getContext('2d'), keyboard = new THREEx.KeyboardState(), + requestAnimFrame = (function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000/60); }; + })(); + + GarageServerIO.initializeGarageServer('http://garageserver_io.jbillmann.c9.io', { logging: true, clientSidePrediction: true, interpolation: true, - onUpdatePlayerPhysics: OnProcessGamePhysics, - onInterpolation: function (currentState, previousState, targetState, amount) { + onUpdatePlayerPhysics: onUpdatePlayerPhysics, + onInterpolation: function (previousState, targetState, amount) { var interpolationState = {}; interpolationState.x = (previousState.x + amount * (targetState.x - previousState.x)); interpolationState.y = (previousState.y + amount * (targetState.y - previousState.y)); return interpolationState; - } - }); - - var gameCanvas = document.getElementById('gameCanvas'), - keyboard = new THREEx.KeyboardState(), - ctxGameCanvas = gameCanvas.getContext('2d'), - - requestAnimFrame = (function () { - return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000/60); }; - })(), - - processClientInput = function () { + }, + onUpdate: function () { if (keyboard.pressed('left')) { GarageServerIO.addPlayerInput('left'); } @@ -33,22 +28,20 @@ $(function () { if (keyboard.pressed('up')) { GarageServerIO.addPlayerInput('up'); } - }, + } + }); - draw = function () { - ctxGameCanvas.clearRect(0, 0, gameCanvas.width, gameCanvas.height); + GarageServerIO.start(); + GarageServerIO.setPlayerState({ x: 0, y: 0 }); + render(); - GarageServerIO.getPlayerStates(function (state) { - ctxGameCanvas.fillRect(state.x, state.y, 15, 15); - }); - }, + function render () { + requestAnimFrame(render); - update = function () { - requestAnimFrame(update); - processClientInput(); - draw(); - $('#fps').html('FPS: ' + GarageServerIO.getFPS()); - }; + ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); - update(); + GarageServerIO.getPlayerStates(function (state) { + ctxCanvas.fillRect(state.x, state.y, 15, 15); + }); + } }); \ No newline at end of file diff --git a/example/shared/core.js b/example/shared/core.js index b34581a..704ba7a 100644 --- a/example/shared/core.js +++ b/example/shared/core.js @@ -1,6 +1,6 @@ (function(exports){ - exports.OnProcessGamePhysics = function (state, inputs, deltaTime) { + exports.onUpdatePlayerPhysics = function (state, inputs, deltaTime) { var i = 0; if (!state.x && !state.y) { diff --git a/example/views/index.jade b/example/views/index.jade index 9ae2f94..b3c5b6c 100644 --- a/example/views/index.jade +++ b/example/views/index.jade @@ -1,5 +1,4 @@ extends layout block content - div#fps canvas#gameCanvas \ No newline at end of file diff --git a/lib/server/garageserver.io.js b/lib/server/garageserver.io.js index 8aded4a..8c39a6c 100644 --- a/lib/server/garageserver.io.js +++ b/lib/server/garageserver.io.js @@ -9,7 +9,7 @@ options = { onPlayerInput: function (socket, input), onPlayerDisconnect: function (socket), onPing: function (socket, data), - onState: function (socket, data), + onPlayerState: function (socket, data), onUpdatePlayerPhysics: function (state, inputs), } */ @@ -39,7 +39,7 @@ GarageServer.prototype.registerSocketEvents = function (options) { socket.on('input', function (data) { if (options.logging) { - console.log('garageserver.io:: socket input ' + socket.id + ' ' + data.input + ' ' + data.seq); + console.log('garageserver.io:: socket input ' + socket.id + ' ' + data[0] + ' ' + data[1]); } self.onPlayerInput(socket, data, options); }); @@ -55,7 +55,7 @@ GarageServer.prototype.registerSocketEvents = function (options) { if (options.logging) { console.log('garageserver.io:: socket playerState ' + data); } - self.onState(socket, data, options); + self.onPlayerState(socket, data, options); }); }); }; @@ -89,10 +89,10 @@ GarageServer.prototype.onPing = function (socket, data, options) { } }; -GarageServer.prototype.onState = function (socket, data, options) { +GarageServer.prototype.onPlayerState = function (socket, data, options) { this.game.setPlayerState(socket, data); - if (options.onState) { - options.onState(socket, data); + if (options.onPlayerState) { + options.onPlayerState(socket, data); } }; diff --git a/lib/server/garageservergame.js b/lib/server/garageservergame.js index 337eec2..a8b2f87 100644 --- a/lib/server/garageservergame.js +++ b/lib/server/garageservergame.js @@ -22,7 +22,7 @@ GarageServerGame.prototype.updatePlayers = function (options) { state = { time: currentTime, playerStates: [] }; this.players.forEach(function (player) { - state.playerStates.push({ id: player.client.id, state: player.state, seq: player.sequence }); + state.playerStates.push([ player.client.id, player.state, player.sequence ]); }); this.players.forEach(function (player) { @@ -54,10 +54,9 @@ GarageServerGame.prototype.addPlayer = function (client) { } }); - // Set player state callback var player = { client: client, - state: {x: 0, y: 0}, + state: {}, inputs: [], sequence: 1 }; @@ -78,7 +77,6 @@ GarageServerGame.prototype.setPlayerState = function (client, state) { this.players.some(function (player) { if (player.client.id === client.id) { player.state = state; - player.sequence += 1; player.inputs = []; return true; } @@ -88,7 +86,7 @@ GarageServerGame.prototype.setPlayerState = function (client, state) { GarageServerGame.prototype.addPlayerInput = function (client, input) { this.players.some(function (player) { if (player.client.id === client.id) { - player.inputs.push(input); + player.inputs.push({ input: input[0], seq: input[1], time: input[2] }); return true; } });