5

I'm making a game with socket.io and nodejs, and I'm making a module called rooms.js, this module require users.js module and fiveSocket.js module but when I call Rooms.New from the main server file, it says that fiveSocket is undefined, same problem when Rooms.New calls a users.js function, I got TypeError: Cannot read property 'getSocketIDbyId' of undefined

rooms.js:

var mysql   = require('../mysql/mysql.js');
var headers = require('./headers.js');
var users   = require('./users.js');
var fiveSocket = require('./sockets.js');
var Rooms = {
    Obj: {},
    Room: function(data) {
        var room = this;
        this.name = data.name;
        this.users = [];
        this.floorCode = data.floor;
        this.description = data.desc;
        this.maxUsers = data.maxUsers;
        this.owner = data.owner;
        this.setTime = new Date().getTime();
        this.dbID = data.dbID;
        this.doorx = data.doorx;
        this.doory = data.doory;
        this.doordir = data.doordir;
    },
    New: function(socketID, roomID) {
        var keys      = Object.keys(Rooms.Obj).length;
        var id        = keys + 1;
        var callback  = function(row) {
            fiveSocket.emitClient(socketID, headers.roomData, {
                title: row.title,
                desc: row.description,
                mapStr: row.floorCode,
                doorx: row.doorx,
                doory: row.doory,
                doordir: row.doordir
            });
            var uid = users.getIdBySocketID(socketID);
            users.Obj[uid].curRoom = roomID;
            var rid = Rooms.getIdByDbID(roomID);
            Rooms.Obj[rid].users.push(uid);
        }
        if(Rooms.getIdByDbID(roomID) != false) {
            var room = Rooms.getIdByDbID(roomID);
            var row = { title: room.name, description: room.description, floorCode: room.foorCode, doorx: room.doorx, doory: room.doory, doordir: room.doordir };
            callback(row);
        } else {
            mysql.Query('SELECT * FROM rooms WHERE id = ? LIMIT 1', roomID, function(rows) {
                if(rows.length > 0) {
                    var row = rows[0];
                    Rooms.Obj[id] = new Rooms.Room({name: row.title, floorCode: row.floorCode, desc: row.description, maxUsers: row.maxUsers, owner: row.owner, dbID: row.id, doorx: row.doorx, doory: row.doory, doordir: row.doordir});
                    callback(row);
                }
            });
        }
    },
    removeUser: function(DBroomID, userID) {
        var rid = Rooms.getIdByDbID(DBroomID);
        var room = Rooms.Obj[rid];
        var index = room.indexOf(userID);
        if (index > -1) array.splice(index, 1);
    },
    Listener: function(users) {
        setInterval(function(){
            for(var roomID in Rooms.Obj) {
                var room = Rooms.Obj[roomID];
                // send users coordinates 
                room.users.forEach(function(uid) {
                    var socketID = users.getSocketIDbyId(uid);
                    var data = Rooms.getUsersInRoomData(roomID);
                    fiveSocket.emitClient(socketID, headers.roomUsers, data);
                });
                // unload inactive rooms (no users after 10 seconds)
                var activeUsers = room.users.length;
                var timestamp = room.setTime;
                var t = new Date(); t.setSeconds(t.getSeconds() + 10);
                var time2 = t.getTime();
                if(activeUsers <= 0 && timestamp < time2) {
                    Rooms.Remove(roomID);
                }
            }
        }, 1);
    },
    getUsersInRoomData: function(roomID) {
        var room = Rooms.Obj[roomID];
        var obj = {};
        room.users.forEach(function(uid) {
            var user = users.Obj[uid];  
            obj[uid] = {
                username: user.username,
                position: user.position,
                figure: user.figure
            };
        });
        return obj;
    },
    Remove: function(id) {
        delete Rooms.Obj[id];
    },
    getIdByDbID: function(dbID) {
        var result = null;
        for(var room in Rooms.Obj) {
            var u = Rooms.Obj[room]; 
            if(u.dbID == dbID) var result = room;
        }
        if(result == null) return false;
        else return result;
    },
    getDbIDbyId: function(id) {
        return Rooms.Obj[id].dbID;
    }
}
Rooms.Listener();
module.exports = Rooms;

EDIT: (if it can be helpful)

When I console.log fiveSocket on the main file enter image description here

When I console.log fiveSocket on the rooms.js file enter image description here

EDIT2: When I've removed var users = require('./users.js'); from fiveSocket, when I console.log it in rooms.js it works, why ?
EDIT3: I still have the problem

If you need the others modules sources:
Users.JS: http://pastebin.com/Ynq9Qvi7
sockets.JS http://pastebin.com/wpmbKeAA

1 Answers1

0

"Rooms" requires "Users" and vice versa, so you are trying to perform "circular dependency".

Quick search for node.js require circular dependencies gives a lot of stuff, for example :

"Circular Dependencies in modules can be tricky, and hard to debug in node.js. If module A requires('B') before it has finished setting up it's exports, and then module B requires('A'), it will get back an empty object instead what A may have intended to export. It makes logical sense that if the export of A wasn't setup, requiring it in B results in an empty export object. All the same, it can be a pain to debug, and not inherently obvious to developers used to having those circular dependencies handled automatically. Fortunately, there are rather simple approaches to resolving the issue."

or How to deal with cyclic dependencies in Node.js

Community
  • 1
  • 1
zamuka
  • 796
  • 2
  • 9
  • 21
  • Thanks for answer, but I didn't understand how to resolve this issue, I understand that I need to restructurate the code, but Is there any easy solution to solve that ? –  Jan 18 '16 at 19:01
  • I think the best way is to remove as many dependencies as you can. In perfect world your "rooms" and "user" should not have dependencies at all (except some basic mixins) but receive necessary data as arguments. In JavaScript you don't have to include "class" definitions to access class methods, just provide the object and use its data. Like you already done in "Listener: function(users) {" – zamuka Jan 19 '16 at 08:45
  • Problem solved, thanks `:)` i've passed the modules in the function –  Jan 19 '16 at 18:08