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?

+3


source to share


3 answers


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);
});

      

+1


source


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.

  1. 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);
    
          

  2. Make Player

    a EventEmitter

    that emits events move

    :

    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});
    });
    
          

  3. 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);
    
          

+2


source


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]();
    }

      

0


source







All Articles