0

I'm trying to use one script for the communal storage of "global" variables, and other scripts can require that script to see those variables, but that appears to not be the right way to do it.

So I have a script called "gamedata.js" that looks like this:

var players = {};
exports.players = players;

In one script:

var gamedata = require('./gamedata');
var players = gamedata.players;
players[player_id] = new player(. . .);
for (var pid in players){
    console.log(pid + ' is online.'); // This runs correctly
}

Then, later, in another script (I know this is later; it's actually in a loop).

var gamedata = require('./gamedata');
var players = gamedata.players;
for (var pid in players){
    // Doesn't even run once
}

Obviously this isn't the right way to do this. How can I do something like this?

Update:

The information necessary to answer this question was not included in this post. For the second example, when it didn't even run once, it was in a different scope, when "players" did in fact mean []. I'm not accepting a correct answer for this because all of the information I included should work correctly, and therefore there cannot be a solution.

River Tam
  • 3,096
  • 4
  • 31
  • 51
  • What isn't working for you? What behavior are you seeing? Something like this does work. – dc5 Aug 14 '13 at 23:58
  • The behavior I'm seeing (sorry, I didn't make this clear) is that players is empty if I get it from another script, even if it's after I put something in players. – River Tam Aug 15 '13 at 00:03
  • After more testing, it appears that this is not entirely true. See my edited post for a more specific problem – River Tam Aug 15 '13 at 00:07
  • Is it because you are using "for (var pid in players)", which is iterating over properties and not for array iteration? – arghbleargh Aug 15 '13 at 00:09
  • @arghbleargh : Good thought. I should have been using an object instead of an Array, you're right. However, I have now changed the players variable to an object (using {} instead of [], which I believe is the correct change) and I'm still having the same problem. – River Tam Aug 15 '13 at 00:11
  • How are you sure that the second script is run later? – arghbleargh Aug 15 '13 at 00:15
  • That should work as well… I have a local version of what you've described running and all works as expected. – dc5 Aug 15 '13 at 00:18
  • Sorry for the waste of time, I solved my problem, and the problem wasn't with anything I'd written. I had missed a parameter, and so players in that particular scope was empty, not in general. – River Tam Aug 15 '13 at 00:18
  • No worries - glad you found the problem. – dc5 Aug 15 '13 at 00:19

2 Answers2

4

Do not attempt to use globals in node.js. Also note that require will reference a cached object and will not actually re-require the same file more than once.

Here's a pretty generic example of how you might start setting up a card game without using global variables

lib/deck.js

var SUITS = ["H", "C", "D", "S"],
    RANKS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];

var Deck = module.exports = function Deck() {
  this.cards = [1, 2, 3, ..., 52];
  this.shuffle();
};

Deck.prototype.shuffle = function shuffle() {
  // shuffle this.cards
};


Deck.prototype.dealCard = function dealCard() {
  var id = this.cards.shift();
  return {id: id, rank: RANKS[id%13], suit: SUITS[id%4]};
};

lib/game.js

var Deck = require("./deck");

var Game = module.exports = function Game(numCards) {
  this.numCards = numCards;
  this.deck = new Deck();
};

Game.prototype.dealCards = function dealCards(player) {
  for (var i=0; i<this.numCards; i++) {
    player.cards.push(this.deck.dealCard());
  }
};

// ...

lib/player.js

var EventEmitter = require("events").EventEmitter;

var Player = module.exports = function Player(name) {
  this.name = name;
  this.cards = [];
};

Player.prototype = Object.create(EventEmitter.prototype, {constructor: {value: Player}};

// ...

lib/session.js

var EventEmitter  = require("events").EventEmitter,
    Game          = require("./game"),
    Player        = require("./player");

var Session = module.exports = function Session(numCards) {
  EventEmitter.call(this);
  this.game = new Game(numCards);
  this.players = [];
  this.scores = [];
};

Session.prototype = Object.create(EventEmitter.prototype, {constructor: {value: Session}});

Session.prototype.addPlayer = function addPlayer(player) {
  // add the player
  this.players.push(player);

  // deal the player some cards
  this.game.dealCards(player);

  // setup event listeners
  player.on("score", function(points) {
    this.addScore(player, points);
  });

  player.on("playerTurn", function(event) {
    // ...
  });
};

Session.prototype.addScore = function(player, points) {
  if (this.scores[player.id] === undefined) {
    this.scores[player.id] = 0;
  }
  this.scores[player.id] += points;
};

run.js

var Session  = require("./session"),
    numCards = 2;

var sessionInstance = new Session(numCards);

sessionInstance.on("addPlayer", function(player) {
  this.addPlayer(player);
});

// e.g.,
// setup a net.Server and fire "addPlayer" when a user connects
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    Quite the example. +1 – tymeJV Aug 15 '13 at 00:47
  • Nice example. I would like to make a distinction between globally accessible variables and globally accessible configuration data. I don't believe there is anything wrong with having a globally accessible place to store configuration settings. For example, you probably shouldn't be pushing your twitter API key though various levels of the architecture just to avoid a good ol' config file. For those who end up here looking for ways to manage configuration check out nconf: https://github.com/flatiron/nconf – Timothy Strimple Aug 15 '13 at 01:20
  • @TimothyStrimple, I agree. I think the distinction there is *dynamic* vs. *static* data. Static configuration data is fine as a (e.g.,) `config.json`, whereas dynamic runtime data should not be global. – Mulan Aug 15 '13 at 13:59
0

One way to accomplish what you are trying to do is to use a singleton type object as your "global" memory space and let's say this file is named NodeGlobalVars.js.

exports.NodeGlobalVars = NodeGlobalVars;

function NodeGlobalVars()
{   
    //only do this if this is the first time the object has been used
    if(typeof(NodeGlobalVars.SingleInstance) === "undefined")
    {
        this.GlobalVariable1 = 1;
        this.GlobalVariable2 = 2;

        //set to this so we won't create more objects later
        NodeGlobalVars.SingleInstance = this;
    }

    return NodeGlobalVars.SingleInstance;   //return the single instance variable
};

Now in other files you want to get that data from and modify it, you can do the following in as many different files as you want:

var NodeGlobalVars= require('../NodeGlobalVars/NodeGlobalVars.js').NodeGlobalVars;

//now get access to the NodeGlobalVars
var Globals = new NodeGlobalVars();

//now log the state of GlobalVariable1 and modify the value of GlobalVariable2
console.log("Global1: " + Globals.GlobalVariable1);
Globals.GlobalVariable2++;

You can modify the data freely from any other file and they will all point back to the same memory. In effect, you have created global memory space for a node application and have done so using a nice convenient like namespace Globals on the front of it to make it apparent that it is global.

Now, whether you should do this or not, that's another question.

Brian
  • 3,264
  • 4
  • 30
  • 43