Node.js and exported variables - node.js

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.

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

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.

Related

nodejs discord get nickname from a users ID

My goals are to obtain the users nickname by using their ID.
Their ID's are stored as variables which are being collected from a reaction collector.
I have tried a few methods and failed, most of which either return nothing or errors.
The below code returns nothing, the getnames() function is empty. This method was recommended to me buy 2 people from a nodejs discord server which aims to help solve issues, similar to here.
// returns player ID's
function getPlayers() {
let players = [];
players.push(queue.tank[0]); // First (1) in TANK queue
players.push(queue.heal[0]); // First (1) in HEAL queue
players.push(queue.dps[0]); // First (2) in DPS queue
players.push(queue.dps[1]);
return players;
}
// get nick names from ID's
function getnames() {
let players = getPlayers();
let playerNicks = [];
let newPlayer = "";
players.forEach(async player => {
newPlayer = await message.guild.members.fetch(player).then(function (user) {return user.displayName });
playerNicks.push(newPlayer)
return playerNicks;
})}
//formats nicknames into string
function formatnicknames() {
let formatted_string2 = '';
let playerNicks = getnames();
if (playerNicks)
formatted_string2 = `${playerNicks[0]} \n${playerNicks[1]} \n${playerNicks[2]} \n${playerNicks[3]}`;
return formatted_string2;
}
I have also tried a few variations of the below code, still unable to obtain nickname.
message.guild.members.cache.get(user.id)
Edit #1
now tried the following code with no success. (boost1ID contains the ID of 1 user)
var mem1 = message.guild.members.fetch(boost1ID).nickname
Edit #2
tried a new method of obtaining displayname from ID.
var guild = client.guilds.cache.get('guildid');
var mem1 = guild.member(boost1ID);
var mem2 = guild.member(boost2ID);
var mem3 = guild.member(boost3ID);
var mem4 = guild.member(boost4ID);
var nickname1 = mem1 ? mem1.displayName : null;
var nickname2 = mem2 ? mem2.displayName : null;
var nickname3 = mem3 ? mem3.displayName : null;
var nickname4 = mem4 ? mem4.displayName : null;
var Allnicknames = `${nickname1} ${nickname2} ${nickname3} ${nickname4}`
message.channel.send(`testing nicknames: ${Allnicknames}`)
I managed to only return my own name since i dont have a nickname on this server, but the other three users who does have a nickname returned null.
This is the simplest solution:
// your users ids
const IDs = [ '84847448498748974', '48477847847844' ];
const promises = IDs.map((userID) => {
return new Promise(async (resolve) => {
const member = message.guild.member(userID) || await message.guild.members.fetch(userID);
resolve(member.displayName || member.user.username);
});
});
const nicknames = await Promise.all(promises);
// you now have access to ALL the nicknames, even if the members were not cached!
The members you are trying to get the nicknames of are not necessarily cached, and this fixes that.
I made an example that could help you.
let testUsers = [];
module.exports = class extends Command {
constructor(...args) {
super(...args, {
description: 'Testing.',
category: "Information",
});
}
async run(message) {
function getNicknames(userArr, guild) {
let playerNicks = [];
for(var i = 0; i < userArr.length; i++) {
playerNicks.push(guild.member(userArr[i]).displayName);
}
return playerNicks;
}
let testUser = message.guild.members.cache.get(message.author.id);
testUsers.push(testUser);
let guild = message.guild;
console.log(getNicknames(testUsers, guild));
}
}
I created a function getNicknames that takes in two parameters. The first one is an Array of users (as you get one from your function getPlayers()) and the second one is the guild you are playing in. You need to provide the guild, because every user should be a GuildMember, because you want to use .displayName. I created a user Array outside of my command code, because otherwise there will only be one user in the Array everytime you use the command. Inside of the getNicknames() function I have created a new Array playerNicks that I basically fill with the user nicknames we get from our provided user Array.
Now you have to implement that into your code.
The call of the function getNicknames(), for your code should look like this:
getNicknames(getPlayers(), message.guild);

declaring variable with same name causes problems in socket io

It took me a while to figure out what the problem was, but I'm wondering why it's acting like that.
Using this code, the variables player, players and socket will be undefined, causing errors.
var player = Player(socket.id, socket);
socket.on('joinHost', function(data) {
var gameID = data;
player.gameID=gameID;
var game = GAME_LIST[gameID];
game.players[socket.id]=player;
var players = game.players;
for (var p in players){
var player = players[p];
var socket = player.socket;
socket.emit('playerJoined');
}
});
Avoiding the declarations of variables with same names makes it all work correctly.
var player = Player(socket.id, socket);
socket.on('joinHost', function(data) {
var gameID = data;
player.gameID=gameID;
var game = GAME_LIST[gameID];
game.players[socket.id]=player;
var tempPlayers = game.players;
for (var p in tempPlayers){
var tempPlayer = tempPlayers[p];
var tempSocket = tempPlayer.socket;
tempSocket.emit('playerJoined');
}
});
The interesting part is, when I ran the first code, it says the player in the line player.gameID=gameID is undefined, while if I removed the code which is after player.gameID=gameID, the player was defined. Basically, the code after player.gameID=gameID caused the player to be undefined.
So, why is this happening?
When you declare var player = players[p]; it is declared for the whole function scope (the for loop doesn't has a scope of it's own).
The names in the current scope are evaluated all in the beginning, before executing the function body.
So when function(data) is called, the name player is overridden in that scope even before var gameID = data; is executed.
A minimal example:
> var x = 'foo';
> f = function() { console.log(x); var x = 'bar'; }
> f()
undefined
Javascript moves variables's declaration to the top of the scope they were defined and gives them an undefined initial value but keeps assignment in place. This is called hoisting
Your code is equivalent to :
var player = Player(socket.id, socket);
socket.on('joinHost', function(data) {
var gameID; // undefined
var game; // undefined
var players; // undefined
var player; // undefined
var socket; // undefined
gameID = data;
player.gameID=gameID; // can't set property 'gameID' of undefined
game = GAME_LIST[gameID];
game.players[socket.id]=player; // is undefined since 'player' is undefined at this stage
players = game.players; // undefined
for (var p in players){
player = players[p];
socket = player.socket;
socket.emit('playerJoined');
}
});

Using knex SELECT query results for another SELECT query

I am trying to run a PostgreSQL query with Knex and then use the results to run another query.
exports.buildBuoyFeaturesJSON = function (conditionA, conditionB) {
var query = null;
var selectedFields = knex.select
(
knex.raw('t_record.id AS id'),
...
knex.raw('t_record.latitude AS latitude'),
knex.raw('t_record.longitude AS longitude')
)
.from('t_record')
.then(function (response) {
var geometry_array = [];
var rows = response.rows;
var keys = [];
for (var key = 0; key <= rows.length - 1; key++) {
var geometry =
{
"id" : rows[key].id,
"type" : "Feature",
"geometry" : rows[key].geometry,
"properties" : {
...
"sensors" : []
}
};
keys.push(rows[key].id);
geometry_array.push(geometry);
}
getMeasurementsAndSensors(keys, geometry_array);
});
};
The latter function uses some of the results from the previous function. Due to asynchronous nature of Knex, I need to call the second function from inside the first function's .then() statement:
function getMeasurementsAndSensors (keys, geometry_array) {
var query = knex
.select
(
't_record_id',
'i_sensor_id',
'description',
'i_measurement_id',
't_sensor_name',
't_measurement_name',
'value',
'units'
)
.from('i_record')
...
.whereRaw('i_record.t_record_id IN (' + keys + ')')
.orderByRaw('t_record_id, i_sensor_id ASC')
.then(function (response) {
var rows = response.rows;
var t_record_id = 0;
var i_sensor_id = 0;
var record_counter = -1;
var sensor_counter = -1;
for (var records = 0; records <= rows.length -1; records++) {
if (t_record_id !== rows[records].t_record_id) {
t_record_id = rows[records].t_record_id;
record_counter++;
sensor_counter = -1;
}
if (i_sensor_id !== rows[records].i_sensor_id) {
i_sensor_id = rows[records].i_sensor_id;
geometry_array[record_counter].properties.sensors[++sensor_counter] =
{
'i_sensor_id' : rows[records].i_sensor_id,
't_sensor_name' : rows[records].t_sensor_name,
'description' : rows[records].description,
'measurements' : []
};
}
geometry_array[record_counter].properties.sensors[sensor_counter].measurements.push
({
'i_measurement_id': rows[records].i_measurement_id,
'measurement_name': rows[records].t_measurement_name,
'value': rows[records].value,
'units': rows[records].units
});
}
//wrapping features with metadata.
var feature_collection = GEOGRAPHY_METADATA;
feature_collection.features = geometry_array;
JSONToFile(feature_collection, 'buoy_features');
});
}
Currently I save end result to a JSON file because I couldn't get Promises to work. JSON is later used to power a small OpenLayers application, hence the JSON-ification after getting results.
I am quite sure that getting the data from a database, saving it to file, then accessing it from another process and using it for OpenLayers is a very redundant way to do it, but so far, it is the only one that works.
I know there are a lot of ways to make these functions work better, but I am new to promises and don't know how to work with them outside of most basic functions. Any suggestions how to make this code better are welcome.
All you appear to be missing is a bunch of returns.
Here are skeletonized versions of the two functions, including the necessary returns :
exports.buildBuoyFeaturesJSON = function(conditionA, conditionB) {
return knex.select (...)
^^^^^^
.from(...)
.then(function(response) {
// synchronous stuff
// synchronous stuff
return getMeasurementsAndSensors(keys, geometry_array);
^^^^^^
}).then(function(geometry_array) {
var feature_collection = GEOGRAPHY_METADATA;
feature_collection.features = geometry_array;
return feature_collection;
^^^^^^
});
};
function getMeasurementsAndSensors(keys, geometry_array) {
return knex.select(...)
^^^^^^
.from(...)
...
.whereRaw(...)
.orderByRaw(...)
.then(function(response) {
// heaps of synchronous stuff
// heaps of synchronous stuff
// heaps of synchronous stuff
return geometry_array;
^^^^^^^^^^^^^^^^^^^^^
});
}
I moved the feature_collection collection part into buildBuoyFeaturesJSON() on the basis that it seems to sit there more logically. If not, then it would be very simple to move it back into getMeasurementsAndSensors().
I have not tried to fix the additional issue highlighted by #vitaly-t.

Node.js while loop return deffered result

What I'm trying to do is this, I have 2 users an attacker and a defender. I want the call hit() function until one of the runs out of Health, hit() should be called on turns, first attacker, then defender, then attacker and so on until one reaches 0 hp.
My idea was to do it with a while loop, with current code all I get is the console.log from hit(), an infinite loop. The data from hit() is returned correctly if it's not inside a loop ... I could simply just work in the while loop and not use the hit function but it would mean repeating a lot of code, since there will be a few things to consider before a hit actually happens.
If you have an alternative to the while loop I'm open to ideas, also I should mention I'm new at node so keep it as simple as possible please. Thank you.
This is the relevant part of the code:
var prepareAttack = function(attacker,defender) {
connectdb().done(function(connection) {
query(connection,'SELECT * FROM members WHERE name = ?', attacker).done(function(result) {
var attackerData = result[0][0]
query(connection,'SELECT * FROM members WHERE name = ?', defender).done(function(result) {
var defenderData = result[0][0]
var attackerHp = attackerData.hp
var defenderHp = defenderData.hp
while(attackerHp > 0 && defenderHp > 0) {
hit(attackerData,defenderData).done(function(result){
defenderHp = result;
console.log(defenderHp)
})
hit(defenderData, attackerData).done(function(result) {
attackerHp = result;
console.log(attackerHp)
})
}
})
})
})
}
var hit = function (attackerData, defenderData) { // simplified code just relevant parts inside.
console.log('hitting')
var defer = Q.defer()
var newHp = 0;
defer.resolve(newHp)
return defer.promise
}

How can I corral a method which may contain it's own async calls without having write access to the file?

I have a set of files, module1.js, module2.js, module3.js, and each of these contains a return object which has property-methods that will be executed. The objects are inspected to determine the property names dynamically, and I can .toString() the methods.
The inner methods will almost certainly contain async calls, like this:
function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
}
How can I marshall these methods from a calling parent method to eventually return the value without actively having writeable access to the files module1.js, module2.js and module3.js. Assume those are cast in stone, and can never be edited. Anything else that's reasonable is fair game. Just please don't say "well, rewrite doSomething to pass in a CB and let the makeDeep be wrapped in the CB". Note that I'm calling this module from my own code, and note that makeAsync is whatever asynchronous methods the module-author wants to call.
IMPORTANT NOTE: I am the one writing makeDeep and I am the one including the module so I can do whatever you want in either of those two places, and makeDeep is injected into the module dynamically (I'm doing a mixin pattern) so if your solution relies on modifying makeDeep to work or something in the "parent" calling method, that is 100% reasonable and I'm all about that.
If this is the case, there's no "need" to have the return keyword before makeDeep but bonus points if the syntax does use those words (that heavily indicates to a developer that that's a code exit point, yes?)
Assume that module1.js looks like:
module.exports = function() {
this.doSomething11 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
}
}
module2.js
module.exports = function() {
this.doSomething21 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
};
this.doSomething22 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
};
}
module3.js
module.exports = function() {
this.doSomething31 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
};
this.doSomething32 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
};
this.doSomething33 = function doSomething(vars){
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that,red,up,one,function(err,res){
return makeDeep(res);
}
}
}
Yes, the examples are contrived, because I'm more focused on concept than I am on actual specifics. They could be triply nested callbacks, or could use some sort of internal callbacks. Mostly I just want to know if there's a way to make this happen.
If not, how can I make it work for the users if I provide them a specific library and have them return into the library?
My goal is to end up replicating something similar to the ASP.NET style ActionResult, and I'm open to the idea of using Q, fibers or promises, but I'm missing something around the invocation to get it back when an async callback is used.
I'll have a go at your problem which only requires the required module to call return that.makeDeep() instead of just return makeDeep(). I know you did not want to change the called code, but hey, you can always use burrito and change those lines dynamically (no write access needed).
calling code
var Controller = require('./module1');
assert(typeof Controller == 'function');
httpServer.on('request', function(req, res) {
// I assume this is something you would do
var vars = processReqParameters(req);
var action = vars.action;
var controller = new Controller();
if(typeof controller[action] === 'function') {
// now I assume that makeDeep will at one point
// call res.end();
var makeDeep = createMakeDeep(req, res, vars) // or other parameters
// this is how we inject makeDeep in the controller
var controllerInstance = Object.create(controller, {makeDeep: makeDeep});
return controllerInstance[action](vars);
}
else {
res.writeHead(404, 'Controller Not Found');
res.end('Too bad\n');
}
})
called code
module.exports = function() {
this.myAction = function(vars) {
var that = this,
red = 'blue',
up = 'down',
one = 2;
makeAsync(that, red, up, one, function(err, res) {
return that.makeDeep(res);
})
}
}

Resources