GarageServer.IO
A simple, lightweight, HTML multiplayer game server (and client) for Node.js
Features
- Authoritative Game Server
- Client Side / Input Prediciton
- Client Side Smooting
- Entity Interpolation
- Server State History
- Server and Client Messaging
Quick Start
Create a quick game whereby players simply move squares along the x-axis. (I know, it's boring, but it keeps things simple.)
Server
1. Create instance of GarageServer.IO - pass in a Socket.IO instance and GarageServer.IO options.
var garageServer = require('garageserver.io'),
var server = garageServer.createGarageServer(sockets,
{
interpolation: true,
clientSidePrediction: true,
worldState: { width: '400px', height: '400px'; }
});
2. Start GarageServer.IO instance prior to starting physics loop. This starts the clock that is used for broadcasting state and storing state history.
server.start();
3. Inside physics loop, process inputs for players, process entites and update states. Note that state is an object literal effectively offering up any grab bag of properties that pertain to your game's state.
var players = server.getPlayers(),
entities = server.getEntities();
players.forEach(function (player) {
var newState = {};
if (!player.state.x) {
player.state.x = 0;
}
for (i = 0; i < player.inputs.length; i ++) {
if (player.inputs[i].input === 'left') {
newState.x = player.state.x - (50 * deltaTime);
} else if (inputs[i].input === 'right') {
newState.x = player.state.x + (50 * deltaTime);
}
}
server.updatePlayerState(player.id, newState);
});
entities.forEach(function (entity) {
// Calculate new state from entity.state and send GarageServer.IO new state
server.updateEntityState(entity.id, newState);
});
Client
1. Initialize GarageServer.IO.
GarageServerIO.initializeGarageServer('http://insertmygameurlhere.com', {
onReady: function () {
// Call your game loop
},
onUpdatePlayerPrediction: function (state, inputs, deltaTime) {
var newState = {};
if (!player.state.x) {
player.state.x = 0;
}
for (i = 0; i < player.inputs.length; i ++) {
if (player.inputs[i].input === 'left') {
newState.x = player.state.x - (50 * deltaTime);
} else if (inputs[i].input === 'right') {
newState.x = player.state.x + (50 * deltaTime);
}
}
return newState;
},
onInterpolation: function (previousState, targetState, amount) {
return { x: (previousState.x + amount * (targetState.x - previousState.x)) };
},
onWorldState: function (state) {
document.getElementById('gameCanvas').style.width = state.width;
document.getElementById('gameCanvas').style.height = state.height;
}
};
2. Inside physics loop, capture and send input via GarageServer.IO.
GarageServerIO.addInput(myInput);
3. Inside render loop, extract player and entity states.
var playerStates = GarageServerIO.getPlayerStates(),
entityStates = GarageServerIO.getEntityStates();
playerStates.forEach(function (player) {
ctxCanvas.fillRect(player.state.x, 0, 5, 5);
});
entityStates.forEach(function (entity) {
ctxCanvas.fillRect(entity.state.x, 0, 5, 5);
});
API
Client
initializeGarageServer
GarageServerIO.initializeGarageServer(path, options)
path
Type: string
options
Type: object literal
onPlayerConnect(callback)
onPlayerDisconnect(callback)
onPlayerReconnect(callback)
onPlayerUpdate(callback(state))
onEntityUpdate(callback(state))
onPlayerRemove(callback(id))
onEntityRemove(callback(id))
onEvent(callback(data))
onWorldState(callback(state))
onPing(callback(pingDelay))
onUpdatePlayerPrediction(callback(state, inputs, deltaTime) : newState)
onInterpolation(callback(previousState, targetState, amount) : newState)
onReady(callback)
logging: true
addInput
GarageServerIO.addInput(input)
input
Type: object literal
getPlayerStates
GarageServerIO.getPlayerStates() : [, {id, state}]
Returns: array
id
Type: string
state
Type: object literal
getEntityStates
GarageServerIO.getEntityStates() : [, {id, state}]
Returns: array
id
Type: string
state
Type: object literal
getId
GarageServerIO.getId() : playerid
Returns: string
playerid
Type: string
sendServerEvent
GarageServerIO.sendServerEvent(data)
data
Type: object literal
Server
createGarageServer
require('garageserver.io').createGarageServer(io, options) : GarageServerIO
/*
options = {
stateInterval: 45,
logging: true,
clientSidePrediction: true,
interpolation: true,
interpolationDelay: 100,
smoothingFactor: 0.3,
pingInterval: 2000,
maxUpdateBuffer: 120,
maxHistorySecondBuffer: 1000,
worldState: {},
onPlayerConnect(callback(socket)),
onPlayerInput(callback(socket, input)),
onPlayerDisconnect(callback(socket)),
onPing(callback(socket, data)),
onEvent(callback(data))
}
*/
io
Type: Socket.IO instance
options
Type: object literal
start
GarageServerIO.start()
stop
GarageServerIO.stop()
getPlayers
GarageServerIO.getPlayers() : [, {id, state, [, inputs], [, {states, executionTimes}]}]
Returns: array
id
Type: string
state
Type: object literal
inputs
Type: array of object literals
stateHistory
Type: array of object literals
getEntities
GarageServerIO.getEntities() : [,{id, state, [, {state, executionTime }]}]
Returns: array
id
Type: string
state
Type: object literal
stateHistory
Type: array of object literals
updatePlayerState
GarageServerIO.updatePlayerState(id, state)
id
Type: string
state
Type: object literal
updateEntityState
GarageServerIO.updateEntityState(id, state)
id
Type: string
state
Type: object literal
addEntity
GarageServerIO.addEntity(id)
id
Type: string
removeEntity
GarageServerIO.removeEntity(id)
id
Type: string
sendPlayerEvent
GarageServerIO.sendPlayerEvent(id, data)
id
Type: string
data
Type: object literal
sendPlayersEvent
GarageServerIO.sendPlayersEvent(data)
data
Type: object literal