Kettlewell's Forum Posts

  • 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.

    /* 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 [ul]
    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 [ul]
    	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());
    		});
    	});
    	
    }[/code:3jvxiztl]
  • I cant say for sure but do you have "pixel rounding set to "Yes" in the properties dialog box?

    That was my first thought and it was turned off but still produces the same issue unfortunately.

  • I'm having a slight issue with ReplaceColor, It's changing the colors when the effect is applied.

    The image is originally pure White/Black but when the effect is used it will have a more varied version of colors as shown in the following images.

    Left is the image with ReplaceColor - Right is a pure image without any effects

    Left is the image with ReplaceColor - Right is the image before the effect

    Does anyone have any idea how I can avoid this issue or why it is occurring?

  • Just working on the system before I bother with graphics.

    Learning as I go along, learned HTML and JS last week but everything is in working order as it stands, just about to cluster the Node server and working on a few kinks with Construct 2.

  • Sprites Z order, layer order or visibility does not interfere if it's clickable or not.

    Instead of using a button over the other, try creating a single sprite to work as your button and then change it's frames depending on how it's toggled.

    Sorry if I can't help any further, but I don't quite understood your problem D:

    I was basically using two sprites on top of each other and making one visible and the other invisible on each click but the sprite on top was never clickable even if the sprite under it was set to invisible.

  • I have a bit of an issue with my code, I've done a simple mute/unmute music sprite.

    Originally I made it a single sprite with multiple frames but even if I had animation set to STOP it would still animate strangely.

    The other issues is once I separated them and placed them on top of each other that I could only click one button. I changed that and slightly overlapped them to see what is happening, it seems that despite being on the same layer, in the order being placed under and being set to invisible, the sound off was not clickable when sound on was placed over it.

    Here is an example in pictures;

    Inside the development environment

    Selectable sound on area

    Selectable sound off area

  • I've been looking up node.js, currently investigating if C2 can operate with it and if it would be capable of dealing with data such as timers.

  • I've been quickly getting to grasp with C2 through testing, tutorials and help and I decided to jump into the deep end with a game design.

    This game basically requires a MySql database which I've established and learned how to communicate with through the use of PHP but my issue is concerning a specific feature of the game. Timers must be used to do specific tasks which make take 5-30 minutes.

    My question is, without a server keeping this running, is it possible to have a timer run for each user which will not be destroyed if they leave the game and they cannot not directly influence through such means as memory editors?

    Thank you

  • Thank you for all the help, I got my code working and managed to edit it to what I wanted.

    Great help again, thank you.

  • You can return a tokenized string or a JSON object that C2 can interpret.

    Could you possibly give me a small example and I'll work the rest out from there, thanks Magi

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I took away the change to an int and replaced the condition checks with strings and it now operates.

    Thank you for the help.

  • Thank you, it's working perfectly.

  • I was using else statements so that other parts of the code didn't run. If it works like a typical if statement I didn't want it to keep comparing the variables but I removed them anyway.

    I converted the value to an int but unfortunately it still won't run. IsMember returns 1 on a successful login and the debug shows it logs in but the sub events just do not seem to want to fire.

  • I've been using a PHP file made from the Login tutorial and it works fine but I've been trying to force an error by creating an account on the login system with an email that already exists, I'm using this code

    <?php header('Access-Control-Allow-Origin: *');
    
    ini_set('display_errors', 'On');
    error_reporting(E_ALL | E_STRICT);
    
    $email = $_GET['femail'];
    $password = $_GET['fpass'];
    $con=mysqli_connect("mysql2.clusterdb.net","PSDB-9qw","bravo190","PSDB-9qw","3306","/var/lib/mysql/cluster2.sock");
    
    // Check connection
    if (mysqli_connect_errno()){
      echo "-1";
     } else {
    	
    	$qz = "SELECT IsMember FROM UserAuthData where email='".$email."' and password='".$password."'"; 
    	$qz = str_replace("\'","",$qz);
    	$result = mysqli_query($con,$qz);
    
    	if (!$result) {
    		echo "-2";
    	} else {
    		while($row = mysqli_fetch_array($result))
    		{
    			echo $row['IsMember'];
    		}
    	}	 
    }
    
    mysqli_close($con);
    ?>[/code:1q9v6mso]
    
    The problem is with my limited knowledge of php and mysql, I expected this to return a -2 value to ajax like it does when there are no errors due to duplicate unique keys in the database but it doesn't, ajax never returns a value.
    
    Would anyone know why it will return IsMember fine but not -2?
    
    Thank you.
  • Is there any particular reason that the sub events are not firing here?

    I've ensured that the variables are tested and are fine but the sub events will just not fire.