NodeJs Server

Discussion and feedback on Construct 2

Post » Tue Jun 23, 2015 4:46 pm

I'm currently developing this server as I learned JS a few weeks ago, it works with websockets in C2.

There is probably a lot I can do better or have done wrong but I thought I would post this here so that other newbies like myself may give it a try.

Code: Select all
/* Node.JS Server */

//INCLUDED MODULES
var WebSocketServer = require('websocket').server; //Websockets
var Http = require('http');  //HTTP Connection
var Cluster = require('cluster');  //Cluster module
var NumCpus = require('os').cpus().length; //Number of CPU cores
var Sqlite3 = require("sqlite3").verbose(); //SQLite3 module

//SETTING VARIABLES
var Port = 8080; //Port the server will use
var ClientAliveTimer = 20000; //Amount of time the server will ping the client to ensure it is connected
var ClientAliveGraceTimer = 10000; //Amount of grace timer after ClientAliveTimer has timed out
var MaxFrameSize = 0x100000; //Max received frame size in bytes
var MaxMsgSize = 0x100000; //Max fragmented message size in bytes

//GLOBAL VARS
var UniqueID = 0; //Incrementing on every connection
var ConnArray = []; //Holds all connections
var NumOfQueries = 0; //Number of SQL queries
var ClustersExiting = 10; //Keep track of clusters exiting

//FILE SYSTEM
var FileSystem = require("fs"); //File System to be used by SQLite3 and the logger
var Dir = './DataFiles'; //Directory of backup folder

//SQLITE3 DATABASE VARIABLES
var dbFile = "./DataFiles/UserDB.db"; //Database filename
var DBExists = FileSystem.existsSync(dbFile); //Check if the Database exists
var DB = new Sqlite3.Database(dbFile); //DB file

//CREATE DATAFILE DIRECTORY
if (!FileSystem.existsSync(Dir)){ FileSystem.mkdirSync(Dir);}

//DB CHECK AND TABLE CREATION
if(!DBExists) {
   
   //USERDATA table
   DB.run(   "CREATE TABLE USERDATA(" +
         "EMAIL CHAR(60) PRIMARY KEY NOT NULL," +
         "PASS CHAR(15) NOT NULL," +
         "LEVEL TINYINT NOT NULL," +
         "EXP INT NOT NULL," +
         "ISMEMBER BOOLEAN NOT NULL," +
         "CHARCREATED BOOLEAN NOT NULL" +
         ")");
         
}

//WRITE TO LOG FILE AND CONSOLE
function EventLogging(LogText) {
   
   console.log(LogText);
   FileSystem.appendFile("./DataFiles/ServerLog.txt", LogText + "\r\n", function(err) {
      if(err) {
         return console.log(err);
      }
   });
}

//RETURN THE FORMATTED DATE
function LogDate(){
   var DateGet = new Date();
   var LogDate = " DATE: " + DateGet.getDate() + "/" + DateGet.getMonth() + "/" + DateGet.getFullYear() + " (" + DateGet.getUTCHours() + ":" + DateGet.getUTCMinutes() + ")";
   return LogDate;
}

//ENCODE AND DECODE UTF8 (MESSAGES ARE RECIEVED IN UTF8)
function Encode_Utf8(string) { return unescape(encodeURIComponent(string)); }
function Decode_Utf8(string) { return decodeURIComponent(escape(string)); }   

//HTTP SERVER
var server = Http.createServer(function(request, response) {});

//WEBSOCKET SERVER
wsServer = new WebSocketServer({
   
   httpServer: server,
   maxReceivedFrameSize: MaxFrameSize,
   maxReceivedMessageSize: MaxMsgSize,
   autoAcceptConnections: false,
   keepalive: true,
   keepaliveInterval: ClientAliveTimer,
   dropConnectionOnKeepaliveTimeout: true,
   keepaliveGracePeriod: ClientAliveGraceTimer
   
});

//SERVER HANDLING [CLOSE]
server.on('close', function() {
   
   //Close all open connections
   for(i=0; i<ConnArray.length; i++){ ConnArray[i].close(); }
   
   var KeyLoop = true;
   while(KeyLoop){
      if (NumOfQueries == 0){
   
         if (DB.open) { DB.close(); }
         EventLogging("[SERVER CLOSING] ID: " + process.pid + LogDate());
         KeyLoop = false;
         
      }
   }
   
});

//SERVER HANDLING [ERROR]
server.on('error', function(err) { EventLogging("[HTTP SERVER ERROR]: ID: " + process.pid + LogDate() + " ERROR: " + err.code); });

//SERVER HANDLING [REQUEST]
server.on('request', function(request, response) { });

//SERVER HANDLING [CONNECTION]
server.on('connection', function(socket) { });

//SERVER HANDLING [CONNECT]
server.on('connect', function(request, socket, head) { });

//CLUSTER HANDLING [EXIT]
Cluster.on('exit', function(worker, code, signal) {
   
   //Close all open connections
   for(i=0; i<ConnArray.length; i++){ ConnArray[i].close(); } //UNSURE IF THIS WORKS
   
   var KeepLoop = true;
   if (typeof (worker.suicide) !== "undefined"){ //If it committed suicide, restart it
      if (worker.suicide === true) {
         
         //Make sure we aren't loosing too many clusters
         ClustersExiting --;
         if (ClustersExiting <= 0){ process.exit(1); };
         
         while(KeyLoop){
            if (NumOfQueries == 0){
      
               if (DB.open) { DB.close(); } //Close the DB connection
               EventLogging("[CLUSTER RESTARTING] ID: " + worker.id + LogDate());
               Cluster.fork(); //Create a new cluster
               KeyLoop = false;
               
            }
         }
      }
   } else { //Else just clear up
      
      while(KeyLoop){
         if (NumOfQueries == 0){
      
            if (DB.open) { DB.close(); } //Close the DB connection
            EventLogging("[CLUSTER CLOSING] ID: " + worker.id + LogDate());
            KeyLoop = false;
            
         }
      }
   }
});

//CLUSTER HANDLING [FORK]
Cluster.on('fork', function(worker) { EventLogging("[CLUSTER FORKED] ID: " + worker.id + LogDate()); });

//CLUSTER HANDLING [DISCONNECT]
Cluster.on('disconnect', function(worker) { EventLogging("[CLUSTER DISCONNECTED] ID: " + worker.id + LogDate()); });

//CLUSTER HANDLING [ONLINE]
Cluster.on('online', function(worker) { EventLogging("[CLUSTER ONLINE] ID: " + worker.id + LogDate()); });

//CLUSTER HANDLING [LISTENING]
Cluster.on('listening', function(worker, address) { });

//CLUSTER HANDLING [SETUP]
Cluster.on('setup', function(settings) { });

//PROCESS HANDLING [SIGTERM]
process.on('SIGTERM', function() {
   server.close(function(){
      process.exit(0);
   });
});

//PROCESS HANDLING [SIGINT]
process.on('SIGINT', function() {
   server.close(function(){
      process.exit(0);
   });
});

//PROCESS HANDLING [EXIT]
process.on('exit', function(code) {
   if (Cluster.isMaster) { EventLogging("[MASTER PROCESS EXIT CODE] ID: " + process.pid + LogDate() + " EXIT CODE: " + code); }
   else { EventLogging("[PROCESS EXIT CODE] ID: " + process.pid + LogDate() + " EXIT CODE: " + code); }
});

//PROCESS HANDLING [UNCAUGHT EXCEPTION]
process.on('uncaughtException', function(err) { EventLogging("[UNCAUGHT EXCEPTION] ID: " + process.pid + LogDate() + " ERROR: " + err.code); });

//LOG THE USER IN
function LogUserIn(UsrEmail, UsrPass, connection){
   
      NumOfQueries ++; //Add to the number of queries
   
      //SQL query to check if the email already exists
      DB.get("SELECT CHARCREATED, EMAIL, PASS from USERDATA where EMAIL=\"" + UsrEmail +"\"", function(err, row){
         
         NumOfQueries --; //Reduce the number of queries
         
         //Check row for a value
         if (typeof row != 'undefined') {
            
            if (row.EMAIL) {
               
               if (row.EMAIL === UsrEmail && row.PASS === UsrPass){ //User and pass match
               
                  if(row.CHARCREATED) { //Character already created
                  
                     connection.email = UsrEmail; //Add the user email to the connection object
                     connection.send("C2"); //return C2 so to login
                     EventLogging("[LOGIN]:" + LogDate() + " EMAIL: " + UsrEmail);
                     
                  } else if(!row.CHARCREATED) { //Character needs creating
                  
                     connection.email = UsrEmail; //Add the user email to the connection object
                     connection.send("C2N"); //return C2N so to login and create character
                     EventLogging("[LOGIN]:" + LogDate() + " EMAIL: " + UsrEmail);   
                     
                  }
                  
               } else if (row.EMAIL === UsrEmail && row.PASS !== UsrPass) { //Email is right but pass is not right
               
                  connection.send("LF2"); //Return LF2 as failed password
                  EventLogging("[LOGIN FAILED [2]]:" + LogDate() + " EMAIL: " + UsrEmail);
               }
            }
            
         } else {
            
            connection.send("LF1"); //Return LF1 as failed email login
            EventLogging("[LOGIN FAILED [1]]:" + LogDate() + " EMAIL: " + UsrEmail);
            
         }
         
         //Error handling
         if (err !== null) {
            
            EventLogging("[SQL ERROR]" + LogDate() + " ERROR: " + err);
            connection.send("SF"); //Report SF for SQL Fault
            
         }
      });
}

//CREATE USER ACCOUNT
function CreateUsrAcc(UsrEmail, UsrPass, connection){
   
   NumOfQueries ++; //Add to the number of queries
   
   //Serialize the database calls
   DB.serialize( function() {
      
      //SQL query to check if the email already exists
      DB.all("SELECT Email from USERDATA where Email=\"" + UsrEmail +"\"", function(err, rows){
      
         NumOfQueries --; //Reduce the number of queries
         
         //Check rows for a value
         if (rows.length > 0) {
            
            if (rows[0].EMAIL) {
               EventLogging("[CREATION ERROR]" + LogDate() + rows[0].EMAIL + " already exists");
               connection.send("CD"); //Return CD so the client knows that the email already exists
            }
            
         } else {
            
            NumOfQueries ++; //Add to the number of queries

            //SQL query to create an account
            var SqlQuery = DB.run("INSERT into USERDATA(EMAIL, PASS, LEVEL, EXP, ISMEMBER, CHARCREATED) VALUES (\"" + UsrEmail + "\",\"" + UsrPass + "\",\"1\",\"0\",\"0\",\"0\")", function(err) {
            
               NumOfQueries --; //Reduce the number of queries
            
               //SQL error checking
               if (err !== null) {
                  EventLogging("[SQL ERROR] " + LogDate() + " ERROR: " + err);
                  connection.send("SF"); //Report SF for SQL Fault
               } else {
                  connection.send("C1"); //Return C1 so client knows the account is created
                  connection.email = UsrEmail; //Add the user email to the connection object
                  EventLogging("[ACCOUNT CREATED]:" + LogDate() + " EMAIL: " + UsrEmail);
               }
            });
         }
         
         //Error handling
         if (err !== null) {
            
            EventLogging("[SQL ERROR]" + LogDate() + " ERROR: " + err);
            connection.send("SF"); //Report SF for SQL Fault
            
         }
      });
   });
}

//CHECK IF ORIGIN IS ALLOWED
function originIsAllowed(origin) {
   //Code to check of origin is allowed//TODO
   return true;
}

//CLUSTERS/CLUSTER MASTER
if (Cluster.isMaster) {
   
   //CREATE OUR CLUSTERS
    for (var i = 0; i < NumCpus; i++) { Cluster.fork(); }
   
} else {

   //SERVER HANDLING [LISTENING]
   server.listen(Port, function() {

      EventLogging("[HTTP SERVER STARTED] ID: " + process.pid  + LogDate() + " is listening on port " + Port);
      
      //Post all command line arguments
      if (process.argv.length > 2) {
         
         //Get all of the arguments into a string
         var TmpString = "";
         for(i=2; i!=process.argv.length; i++){
            TmpString = TmpString + process.argv[i] + " ";
         }
         EventLogging("[COMMAND LINE ARGUMENTS] " + TmpString);
         
      }
   });

   //WEBSOCKET HANDLING
   wsServer.on('request', function(request) {

      //Is the origin allowed
      if (!originIsAllowed(request.origin)) {
         
         EventLogging("[LOGIN REJECTED]:" + LogDate() + " ORIGIN: " + request.origin);
         request.reject(); //Reject the connection
         return;
         
      }
      
      var connection = request.accept(null, request.origin); //Accept the connection
      ConnArray.push(connection); //Add the client to the array
      UniqueID ++; //Increment the UID
      EventLogging("[CONNETION ACCEPTED]:" + LogDate() + " ORIGIN: " + request.origin);
      
      //MESSAGE HANDLING
      connection.on('message', function(message) {
         
         if (message.type === 'utf8') { //Ensure the message is in UTF8
         
            EventLogging("[MESSAGE]: " + LogDate() + message.utf8Data);
            var TmpMsg = (Decode_Utf8(message.utf8Data)); //Decode the message data
            var MsgData = TmpMsg.split(","); //Split the message for the data
            
            //Check what the client has requested
            switch(MsgData[0]) {
               
               case "C1": //Create account
                  CreateUsrAcc(MsgData[1],MsgData[2], connection);
                  break;
                  
               case "C2": //Login
                  LogUserIn(MsgData[1],MsgData[2], connection)
                  break;
                  
            }
         }
      });

      //CONNECTION HANDLING [ERROR]
      connection.on('error', function(err) {
         EventLogging("[CLIENT ERROR]:" + LogDate() + " " + err);
      });
      
      //CONNECTION HANDLING [CLOSE]
      connection.on('close', function(connection) {
         EventLogging("[CLIENT CLOSED]:" + LogDate());
      });
   });
   
}
B
4
Posts: 19
Reputation: 240

Return to Construct 2 General

Who is online

Users browsing this forum: sunisa2532 and 22 guests