Fixed client prediction and prepped for interpolation

This commit is contained in:
Jeremiah Billmann
2013-06-25 22:33:38 -04:00
parent bbbc28e134
commit 3d8fe0790f
5 changed files with 103 additions and 103 deletions
+72 -88
View File
@@ -6,9 +6,9 @@ options = {
onPing: function (data),
logging: true,
clientSidePrediction: true,
onUpdatePhysics: function (state, inputs),
onUpdatePlayerPhysics: function (state, inputs),
interpolation: true,
interpolationDelay: 100,
onInterpolation: function(statePrevious, stateTarget, amount)
pingInterval: 2000
}
*/
@@ -17,14 +17,12 @@ window.GarageServerIO = (function (window, socketio) {
var io = socketio,
socket = null,
sequenceNumber = 0,
processedSequenceNumber = 0,
sequenceNumber = 1,
players = [],
inputs = [],
updates = [],
currentState = {},
options = null,
pingDelay = 100,
serverOffset,
// TODO: DONE CALLBACK
connectToGarageServer = function (path, opts) {
@@ -36,16 +34,15 @@ window.GarageServerIO = (function (window, socketio) {
registerSocketEvents = function () {
socket.on('update', function(data) {
updatePlayerInput(data);
updateState(data);
if (options.logging) {
console.log('garageserver.io:: socket update ' + data);
//console.log('garageserver.io:: socket state update');
}
});
socket.on('ping', function(data) {
pingDelay = new Date().getTime() - data.pingTime;
serverOffset = new Date().getTime() - data.serverTime + pingDelay;
pingDelay = new Date().getTime() - data;
if (options.logging) {
console.log('garageserver.io:: socket ping delay ' + pingDelay + ', server offset ' + serverOffset);
console.log('garageserver.io:: socket ping delay ' + pingDelay);
}
});
socket.on('removePlayer', function(id) {
@@ -65,70 +62,75 @@ window.GarageServerIO = (function (window, socketio) {
socket.emit('ping', new Date().getTime());
}, interval);
},
updateState = function (data) {
updatePlayerState(data);
updateEntityState(data);
},
updatePlayerInput = function (data) {
var playerFound = false,
updateFound = false,
updatePlayerState = function (data) {
var updateFound = false,
playerFound = false,
playerIdx = 0,
updateIdx = 0;
updateIdx = 0,
stateIdx = 0,
playerState;
if (socket.socket.sessionid === data.id) {
for (updateIdx = 0; updateIdx < updates.length; updateIdx ++) {
if (updates[updateIdx].seq === data.seq) {
updates[updateIdx].state = data.state;
updateFound = true;
break;
}
}
if (!updateFound) {
updates.push(data);
}
if (options.clientSidePrediction) {
for (updateIdx = 0; updateIdx < updates.length; updateIdx ++) {
if (updates[updateIdx].seq == processedSequenceNumber) {
updates.splice(0, updateIdx);
}
}
for (updateIdx = 0; updateIdx < inputs.length; updateIdx ++) {
if (inputs[updateIdx].seq == processedSequenceNumber) {
inputs.splice(0, updateIdx + 1);
}
}
if (updates.length > 0 && inputs.length > 0) {
options.onUpdatePhysics(updates[0].state, inputs);
}
}
} else {
for (playerIdx = 0; playerIdx < players.length; playerIdx ++) {
if (players[playerIdx].id === data.id) {
playerFound = true;
for (updateIdx = 0; updateIdx < players[playerIdx].updates.length; updateIdx ++) {
if (players[playerIdx].updates[updateIdx].seq === data.seq) {
players[playerIdx].updates[updateIdx].state = data.state;
updateFound = true;
for(stateIdx = 0; stateIdx < data.playerStates.length; stateIdx ++) {
playerFound = false;
updateFound = false;
playerState = data.playerStates[stateIdx];
if (socket.socket.sessionid === playerState.id) {
currentState = playerState.state;
if (options.clientSidePrediction) {
for (updateIdx = 0; updateIdx < inputs.length; updateIdx ++) {
if (inputs[updateIdx].seq == playerState.seq) {
inputs.splice(0, updateIdx + 1);
break;
}
}
if (!updateFound) {
players[playerIdx].updates.push({ state: data.state, seq: data.seq, timestamp: data.timestamp });
if (inputs.length > 0) {
currentState = options.onUpdatePlayerPhysics(currentState, inputs);
}
break;
}
} else {
for (playerIdx = 0; playerIdx < players.length; playerIdx ++) {
if (players[playerIdx].id === playerState.id) {
playerFound = true;
for (updateIdx = 0; updateIdx < players[playerIdx].updates.length; updateIdx ++) {
if (players[playerIdx].updates[updateIdx].seq === playerState.seq) {
players[playerIdx].updates[updateIdx].state = playerState.state;
updateFound = true;
break;
}
}
if (!updateFound) {
players[playerIdx].updates.push({ state: playerState.state, seq: playerState.seq, time: playerState.time });
}
break;
}
}
if (!playerFound) {
var player = {
id: playerState.id,
updates: []
};
player.updates.push({ state: playerState.state, seq: playerState.seq, time: playerState.time });
players.push(player);
}
}
if (!playerFound) {
var player = {
id: data.id,
updates: []
};
player.updates.push({ state: data.state, seq: data.seq });
players.push(player);
if (options.onPlayerUpdate) {
options.onPlayerUpdate(playerState);
}
}
if (options.onPlayerUpdate) {
options.onPlayerUpdate(data);
}
},
updateEntityState = function (data) {
},
removePlayer = function (id) {
@@ -145,26 +147,14 @@ window.GarageServerIO = (function (window, socketio) {
},
addPlayerInput = function (clientInput) {
var currentState = {},
newState = {},
inputsToProcess = [];
if (options.clientSidePrediction) {
for (var i = 0; i < updates.length; i ++) {
if (updates[i].seq == processedSequenceNumber) {
currentState = updates[i].state;
}
}
}
var inputsToProcess = [];
sequenceNumber += 1;
inputs.push({ input: clientInput, seq: sequenceNumber });
if (options.clientSidePrediction && options.onUpdatePhysics) {
if (options.clientSidePrediction && options.onUpdatePlayerPhysics) {
inputsToProcess.push({ input: clientInput });
newState = options.onUpdatePhysics(currentState, inputsToProcess);
processedSequenceNumber += 1;
updates.push({ state: newState, seq: processedSequenceNumber });
currentState = options.onUpdatePlayerPhysics(currentState, inputsToProcess);
}
sendPlayerInput(clientInput);
},
@@ -178,22 +168,16 @@ window.GarageServerIO = (function (window, socketio) {
for (var playerIdx = 0; playerIdx < players.length; playerIdx ++) {
if (players[playerIdx].updates.length > 0) {
if (options.interpolation) {
for (var updateIdx = players[playerIdx].updates.length - 1; updateIdx >= 0; updateIdx --) {
if (players[playerIdx].updates[updateIdx].timestamp > (new Date().getTime() - serverOffset)) {
stateCallback(players[playerIdx].updates[updateIdx].state);
break;
}
}
} else {
maxUpdate = players[playerIdx].updates.length - 1;
stateCallback(players[playerIdx].updates[maxUpdate].state);
}
}
}
if (updates.length > 0) {
maxUpdate = updates.length - 1;
stateCallback(updates[maxUpdate].state);
}
stateCallback(currentState);
};
return {
+1 -1
View File
@@ -45,7 +45,7 @@ sockets.set('log level', 0);
garageServer.createGarageServer(sockets,{
logging: true,
onUpdatePhysics: function (state, inputs) {
onUpdatePlayerPhysics: function (state, inputs) {
var i = 0;
if (!state.x && !state.y) {
state.x = 0;
+1 -1
View File
@@ -3,7 +3,7 @@ $(function () {
logging: true,
clientSidePrediction: true,
//interpolation: true,
onUpdatePhysics: function (state, inputs) {
onUpdatePlayerPhysics: function (state, inputs) {
var i = 0;
if (!state.x && !state.y) {
state.x = 0;
+3 -3
View File
@@ -2,14 +2,14 @@ var garageServerGame = require('./garageservergame');
/*
options = {
onUpdatePhysics: function (state, inputs),
physicsInterval: 15,
playersInterval: 45,
stateInterval: 45,
logging: true,
onPlayerConnect: function (socket),
onPlayerInput: function (socket, input),
onPlayerDisconnect: function (socket),
onPing: function (socket, data),
onUpdatePlayerPhysics: function (state, inputs),
}
*/
@@ -74,7 +74,7 @@ GarageServer.prototype.onPlayerInput = function (socket, input, options) {
};
GarageServer.prototype.onPing = function (socket, data, options) {
socket.emit('ping', { 'pingTime': data, 'serverTime': new Date().getTime() });
socket.emit('ping', data);
if (options.onPing) {
options.onPing(socket, data);
}
+26 -10
View File
@@ -2,27 +2,42 @@ function GarageServerGame(options) {
var self = this;
this.players = [];
this.entities = [];
this.options = options;
this.startTime = new Date().getTime();
this.physicsIntervalId = setInterval(function () { self.updatePhysics(options); }, options.physicsInterval ? options.physicsInterval : 15);
this.playersIntervalId = setInterval(function () { self.updatePlayers(options); }, options.playersInterval ? options.playersInterval : 45);
this.stateIntervalId = setInterval(function () { self.updateState(options); }, options.stateInterval ? options.stateInterval : 45);
}
GarageServerGame.prototype.updateState = function (options) {
this.updatePlayers(options);
this.updateEntities(options);
};
GarageServerGame.prototype.updatePlayers = function (options) {
var currentTime = new Date().getTime();
for (var i = 0; i < this.players.length; i ++) {
if(this.players[i].inputs.length > 0) {
this.players[i].client.emit('update', { id: this.players[i].client.id, state: this.players[i].state, seq: this.players[i].sequence, timestamp: currentTime });
this.players[i].client.broadcast.emit('update', { id: this.players[i].client.id, state: this.players[i].state, seq: this.players[i].sequence, timestamp: currentTime });
}
var currentTime = new Date().getTime() - this.startTime,
state = { time: currentTime, playerStates: [] },
i = 0;
for (i = 0; i < this.players.length; i ++) {
state.playerStates.push({ id: this.players[i].client.id, state: this.players[i].state, seq: this.players[i].sequence });
}
for (i = 0; i < this.players.length; i ++) {
this.players[i].client.emit('update', state);
}
};
GarageServerGame.prototype.updateEntities = function (options) {
};
GarageServerGame.prototype.updatePhysics = function (options) {
for (var i = 0; i < this.players.length; i ++) {
if (this.players[i].inputs.length > 0) {
if (this.options.onUpdatePhysics) {
this.players[i].state = this.options.onUpdatePhysics(this.players[i].state, this.players[i].inputs);
if (this.options.onUpdatePlayerPhysics) {
this.players[i].state = this.options.onUpdatePlayerPhysics(this.players[i].state, this.players[i].inputs);
}
this.players[i].sequence += this.players[i].inputs.length;
this.players[i].inputs = [];
@@ -37,9 +52,10 @@ GarageServerGame.prototype.addPlayer = function (client) {
}
}
// Set player state callback
var player = {
client: client,
state: {},
state: {x: 0, y: 0},
inputs: [],
sequence: 1
};