diff --git a/README.md b/README.md index bdb0c37..a40b472 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A simple, lightweight, HTML multiplayer game server (and client) for Node.js - Server State History - Server and Client Messaging - Server Reconciliation +- Region Broadcasting - Works with any rendering and/or physics engine ## Install diff --git a/documentation/ServerAPI.md b/documentation/ServerAPI.md index d9925cf..987376e 100644 --- a/documentation/ServerAPI.md +++ b/documentation/ServerAPI.md @@ -236,4 +236,30 @@ GarageServerIO.sendPlayersEvent(data) ``` Allows server to broadcast events to all players. Use this to make custom calls to GarageServer.IO clients for your game. `data` **object literal** -Object containing all properties specific to the custom event. \ No newline at end of file +Object containing all properties specific to the custom event. +#### setPlayerRegion +--- +```js +GarageServerIO.setPlayerRegion(id, region) +``` +Sets the player region. GarageServer.IO will broadcast the state of players and entities who share the same region. NOTE: This will implicitly enable GarageServer.IO region broadcasting - only those players and entities with regions with be notified of state. Use `clearRegions` to revert region broadcasting. +`id` **string** +Id of the player to receive event. +`region` **string** +Name of the region. +#### setEntityRegion +--- +```js +GarageServerIO.setEntityRegion(id, region) +``` +Sets the entity region. GarageServer.IO will broadcast the state of players and entities who share the same region. NOTE: This will implicitly enable GarageServer.IO region broadcasting - only those players and entities with regions with be notified of state. Use `clearRegions` to revert region broadcasting. +`id` **string** +Id of the entity to receive event. +`region` **string** +Name of the region. +#### clearRegions +--- +```js +GarageServerIO.clearRegions() +``` +Clears all regions associated with players and entities. GarageServer.IO will default back to broadcasting state to all players. \ No newline at end of file diff --git a/lib/controllers/entitycontroller.js b/lib/controllers/entitycontroller.js index 11f12d8..fa09987 100644 --- a/lib/controllers/entitycontroller.js +++ b/lib/controllers/entitycontroller.js @@ -32,5 +32,10 @@ EntityController.prototype = { return; } } + }, + clearRegions: function () { + for (var i = 0; i < this.entities.length; i ++) { + this.entities[i].setRegion(''); + } } }; \ No newline at end of file diff --git a/lib/controllers/playercontroller.js b/lib/controllers/playercontroller.js index ceff25e..ae6d822 100644 --- a/lib/controllers/playercontroller.js +++ b/lib/controllers/playercontroller.js @@ -9,11 +9,11 @@ function PlayerController (maxHistorySecondBuffer) { PlayerController.prototype = Object.create(entityController.prototype); -PlayerController.prototype.add = function (client) { +PlayerController.prototype.add = function (socket) { var newPlayer, playerFound = false; this.entities.some(function (player) { - if (player.client.id === client.id) { + if (player.id === socket.id) { newPlayer = player; playerFound = true; return true; @@ -21,7 +21,7 @@ PlayerController.prototype.add = function (client) { }); if (!playerFound) { - newPlayer = new player(client, this.maxHistorySecondBuffer); + newPlayer = new player(socket, this.maxHistorySecondBuffer); this.entities.push(newPlayer); } return newPlayer; @@ -29,7 +29,7 @@ PlayerController.prototype.add = function (client) { PlayerController.prototype.addInput = function (id, input, sequence, time) { this.entities.some(function (player) { - if (player.client.id === id) { + if (player.id === id) { player.inputs.push({ input: input, seq: sequence, time: time }); return true; } diff --git a/lib/entities/entity.js b/lib/entities/entity.js index 8578e97..b57f88b 100644 --- a/lib/entities/entity.js +++ b/lib/entities/entity.js @@ -8,6 +8,7 @@ function Entity (id, maxHistorySecondBuffer) { this.id = id; this.maxHistorySecondBuffer = maxHistorySecondBuffer; this.stateHistory = []; + this.region = ''; } Entity.prototype = { @@ -31,5 +32,8 @@ Entity.prototype = { if (spliceTo > 0) { this.stateHistory.splice(0, spliceTo); } + }, + setRegion: function (region) { + this.region = region; } }; \ No newline at end of file diff --git a/lib/entities/player.js b/lib/entities/player.js index 38c1186..7c5cd32 100644 --- a/lib/entities/player.js +++ b/lib/entities/player.js @@ -2,14 +2,20 @@ var entity = require('./entity'); exports = module.exports = Player; -function Player (client, maxHistorySecondBuffer) { - entity.call(this, client.id, maxHistorySecondBuffer); - this.client = client; +function Player (socket, maxHistorySecondBuffer) { + entity.call(this, socket.id, maxHistorySecondBuffer); + this.socket = socket; this.inputs = []; } Player.prototype = Object.create(entity.prototype); +Player.prototype.setRegion = function (region) { + this.socket.join(region); + this.socket.leave(this.region); + this.region = region; +}; + Player.prototype.addState = function (state, executionTime) { this.addHistory(state, executionTime); this.sequence += this.inputs.length; diff --git a/lib/garageserver.io.js b/lib/garageserver.io.js index 2d4e9be..1b74e94 100644 --- a/lib/garageserver.io.js +++ b/lib/garageserver.io.js @@ -37,8 +37,12 @@ function GarageServer(socketio, options) { this.socketPath = namespace; this.io = socketio; this.registerSocketEvents(options); - this.game = new garageServerGame(options, function (state) { - socketio.of(namespace).emit('update' ,state); + this.game = new garageServerGame(options, function (state, region) { + if (!region) { + socketio.of(namespace).emit('update' ,state); + } else { + socketio.of(namespace).in(region).emit('update' ,state); + } }); } @@ -163,6 +167,18 @@ GarageServer.prototype.sendPlayersEvent = function (data) { this.io.of(this.socketPath).emit('event', data); }; +GarageServer.prototype.setPlayerRegion = function (id, region) { + this.game.setPlayerRegion(id, region); +}; + +GarageServer.prototype.setEntityRegion = function (id, region) { + this.game.setEntityRegion(id, region); +}; + +GarageServer.prototype.clearRegions = function () { + this.game.clearRegions(); +}; + exports.createGarageServer = function (io, options) { return new GarageServer(io, options); }; \ No newline at end of file diff --git a/lib/garageservergame.js b/lib/garageservergame.js index 039c720..e49fcbf 100644 --- a/lib/garageservergame.js +++ b/lib/garageservergame.js @@ -11,6 +11,7 @@ function GarageServerGame(options, broadcastCallback) { this.playerController = new playerController(this.options.maxHistorySecondBuffer ? this.options.maxHistorySecondBuffer : 1000); this.entityController = new entityController(this.options.maxHistorySecondBuffer ? this.options.maxHistorySecondBuffer : 1000); this.broadcastCallback = broadcastCallback; + this.regions = []; } GarageServerGame.prototype.start = function () { @@ -25,13 +26,20 @@ GarageServerGame.prototype.stop = function () { }; GarageServerGame.prototype.broadcastState = function () { - var currentTime = new Date().getTime() - this.startTime, + var i = 0, currentTime = new Date().getTime() - this.startTime, state = { time: currentTime, playerStates: [], entityStates: [] }; - state.playerStates = this.getState(this.playerController); - state.entityStates = this.getState(this.entityController); - - this.broadcastCallback(state); + if (this.regions.length > 0) { + for (i = 0; i < this.regions.length; i ++) { + state.playerStates = this.getStateByRegion(this.playerController, this.regions[i]); + state.entityStates = this.getStateByRegion(this.entityController, this.regions[i]); + this.broadcastCallback(state, this.regions[i]); + } + } else { + state.playerStates = this.getState(this.playerController); + state.entityStates = this.getState(this.entityController); + this.broadcastCallback(state); + } }; GarageServerGame.prototype.getState = function (controller) { @@ -42,6 +50,16 @@ GarageServerGame.prototype.getState = function (controller) { return states; }; +GarageServerGame.prototype.getStateByRegion = function (controller, region) { + var states = []; + controller.entities.forEach(function (entity) { + if (entity.region === region) { + states.push([ entity.id, entity.state, entity.sequence ]); + } + }); + return states; +}; + GarageServerGame.prototype.getPlayers = function () { var list = []; this.playerController.entities.forEach(function (player) { @@ -58,18 +76,6 @@ GarageServerGame.prototype.getEntities = function () { return list; }; -GarageServerGame.prototype.getPlayer = function (id) { - var playerFound; - - this.playerController.entities.some(function (player) { - if (player.id === id) { - playerFound = player; - return true; - } - }); - return playerFound; -}; - GarageServerGame.prototype.updatePlayerState = function (id, state) { this.updateState(this.playerController, id, state); }; @@ -89,8 +95,8 @@ GarageServerGame.prototype.updateState = function (controller, id, state) { }); }; -GarageServerGame.prototype.addPlayer = function (client) { - this.playerController.add(client); +GarageServerGame.prototype.addPlayer = function (socket) { + this.playerController.add(socket); }; GarageServerGame.prototype.removePlayer = function (id) { @@ -112,8 +118,46 @@ GarageServerGame.prototype.addPlayerInput = function (id, input, sequence, time) GarageServerGame.prototype.sendPlayerEvent = function (id, data) { this.playerController.entities.some(function (player) { if (player.id === id) { - player.client.emit('event', data); + player.socket.emit('event', data); return true; } }); +}; + +GarageServerGame.prototype.setPlayerRegion = function (id, region) { + this.setRegion(this.playerController, id, region); +}; + +GarageServerGame.prototype.setEntityRegion = function (id, region) { + this.setRegion(this.entityController, id, region); +}; + +GarageServerGame.prototype.setRegion = function (controller, id, region) { + var self = this; + controller.entities.some(function (entity) { + if (entity.id === id) { + entity.setRegion(region); + self.updateRegions(region); + return true; + } + }); +}; + +GarageServerGame.prototype.updateRegions = function (region) { + var regionFound = false; + + this.regions.forEach(function (item) { + if (item === region) { + regionFound = true; + } + }); + if (!regionFound) { + this.regions.push(region); + } +}; + +GarageServerGame.prototype.clearRegions = function () { + this.regions = []; + this.entityController.clearRegions(); + this.playerController.clearRegions(); }; \ No newline at end of file