How to remove a clutch without duplicating code?
I am making a server for a multiplayer game and am having some problems figuring out how to make a logical modular one. When the player moves, I want to update the object, update the database record, and fix the transition to all connected clients. Now I do it like this:
var socket = require('./socket');
var db = require('./db');
function Player(id, x, y) {
this.id = id;
this.x = x;
this.y = y;
}
Player.prototype.move = function(x, y) {
this.x = x;
this.y = y;
db.update({id: this.id}, {x: x, y: y});
socket.emit('player moved', {x: x, y: y});
}
This tightly connects the socket and database to the player object that feels wrong. However, I don't want to do db.update and socket.emit in the game loop every time I update the player object.
What is the correct way to do this?
source to share
I would do two things:
1) you have events related to the Player class. This allows you to add more triggers in the future without changing the Player class
2) Use pos object to store x and y values. This makes the code more concise.
// In Player file
var EventEmitter = require('events').EventEmitter;
function Player(id, pos) {
this.id = id;
this.pos = pos;
}
Player.events = new EventEmitter();
Player.prototype.move = function (pos) {
Player.events.emit('move', this);
};
module.exports = Player;
// In database file...
var db = require('./db');
var Player = require('./Player');
Player.events.on('move', function (player) {
db.update({id: player.id}, player.pos);
});
// In socket file...
var socket = require('./socket');
var Player = require('./Player');
Player.events.on('move', function (player) {
socket.emit('player moved', player.pos);
});
source to share
If the only purpose of the class Player
is to handle these kinds of updates, then the only real change I see is parameterizing the player code rather than directly including it in db and socket. But this is a very small change as the required calls are already somewhat decoupled.
I see three ways to do this:
-
Make arguments for them
Player
:function Player(id, x, y, db, socket) { this.id = id; this.x = x; this.y = y; this.db = db; this.socket = socket; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.db.update({id: this.id}, {x: x, y: y}); this.socket.emit('player moved', {x: x, y: y}); };
But you get these links in every object.
-
Make them arguments to the builder function that creates the constructor
Player
exports.buildPlayerClass = function(db, socket) { function Player(id, x, y) { this.id = id; this.x = x; this.y = y; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; db.update({id: this.id}, {x: x, y: y}); socket.emit('player moved', {x: x, y: y}); }; };
Usage in the calling module:
var socket = require('./socket'); var db = require('./db'); var Player = require('./player').buildPlayerClass(db, socket); // ... var p = new Player("some-id", 0, 0);
-
Make
Player
aEventEmitter
that emits eventsmove
:var EventEmitter = require('events').EventEmitter; function Player(id, x, y) { EventEmitter.call(this); this.id = id; this.x = x; this.y = y; } Player.prototype = Object.create(EventEmitter.prototype); Player.prototype.constructor = Player; Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.emit('move', {p: this, x: x, y: y}); };
Using:
var socket = require('./socket'); var db = require('./db'); // ... var p = new Player("some-id", 0, 0); p.on('move', function(e) { db.update({id: e.p.id}, {x: e.x, y: e.y}); socket.emit('player moved', {x: e.x, y: e.y}); });
-
Make
Player
use of a regular event:function Player(id, x, y, emitter) { this.id = id; this.x = x; this.y = y; this.emitter = emitter; } Player.prototype.move = function(x, y) { this.x = x; this.y = y; this.emitter.emit('move', {p: this, x: x, y: y}); };
Using:
var playerEvents = new EventEmitter(); playerEvents.on('move', function(e) { db.update({id: e.p.id}, {x: e.x, y: e.y}); socket.emit('player moved', {x: e.x, y: e.y}); }); // ... var p1 = new Player("p1", 0, 0, playerEvents); var p2 = new Player("p2", 0, 0, playerEvents);
source to share
I am very interested in seeing proposals for a solution to this situation. My personal suggestion
Player.prototype.observers = [];
Player.prototype.observers.push(
function(){
if(conditionToUpdateDb){
db.update({id: this.id}, {x: this.x, y: this.y});
}
});
Player.prototype.observers.push(
function(){
if(conditionToEmit){
socket.emit('player moved', {x: this.x, y: this.y});
}
});
and then
Player.prototype.move = function(x, y) {
this.x = x;
this.y = y;
for(key in this.observers){
this.observers[key]();
}
}
If you need different observers
for different Players
, you can also do
function Player(id, x, y, observers) {
this.id = id;
this.x = x;
this.y = y;
this.observers = observers;
}
and then
for(key in this.observers){
this.observers[key]();
}
source to share