Network programming with SFML and Node.js: Part 2

28. February 2012

 

 

Picking up where our last section left off, by the end of this chapter you will be able to send a high score across the wire and process it in node.  In order to do this, we need to create a common format both our client and server understand.  In this particular case we are going to use JSON.  If you have done any recent web program, you are probably familiar with JSON already.  Basically JSON is a micro-format designed for transferring data on the web with a lighter footprint than XML.  All told it is a pretty simple format, here is the JSON we are going to use for storing high scores:

 

 

{ "Scores" : [ {"Name" : "Mike", "Score" : 2}, {"Name" :"Bob", "Score" : 14}, {"Name" :"Steve", "Score" : 12}, {"Name" :"John", "Score" : 10}, {"Name" :"Henry", "Score" : 8} ] }

Click here to download Highscores.txt

 

This JSON represents an object named “Scores” composed of an array of 5 objects that in turn are made up of a string field “Name” and a integer field “Score”.  Javascript and JSON go together like peanut butter and jam, but what about C++?  Well, you could encode your data into a string with very little effort ( one of the big advantageous of JSON ), but “little effort” is still effort, and I’m a lazy guy!  Therefore we are going to use an existing library.  I wanted a light weight and extremely simple JSON library, so I went with the aptly named SimpleJSON.  Installation really couldn’t be simple, just add the 4 cpp files ( 2 headers, 2 source ) to your project and you are done.

 

 

Now lets take a look at our SFML client.  It is going to be a simple command line utility for now, from a dos prompt simply pass in the name and high score as parameters, and it will send them across to the node server.  Lets take a look at Scoreboard.cpp:

 

#include "SFML/Network.hpp" #include "JSON.h" #include <iostream> int main(int argc, char* argv[]) { if(argc != 3) { std::cout << "Invalid usage, proper format is player name then score, for example:" << std::endl; std::cout << "Scoreboard \"Player Name\" 42" << std::endl; return -1; } sf::IPAddress ip("127.0.0.1"); sf::SocketUDP socket; sf::Packet packet; JSONObject data; data[L"action"] = new JSONValue(L"AddScore"); data[L"name"] = new JSONValue(std::wstring(argv[1],argv[1] + strlen(argv[1]))); data[L"score"] = new JSONValue(atof(argv[2])); JSONValue * val = new JSONValue(data); data.clear(); std::wstring dataString = val->Stringify(); delete val; std::string notSoWide; notSoWide.assign(dataString.begin(),dataString.end()); packet.Append(notSoWide.c_str(),notSoWide.length()); unsigned short port = 1000; if(socket.Send(packet,ip,port) != sf::Socket::Done) { std::cout << "An error ocurred sending packet" << std::endl; } socket.Close(); return 0; }

Click here to download scoreboard.cpp

 

 

One annoyance of the library I chose for JSON is it works with UTF-8 wide strings, but the string that we send we want encoded as standard ascii, so there is a bit of gunk as we create the JSON object using wide character strings, then after turning it into a JSON string, we encode it back to ascii.  Otherwise the code is quite straight forward.

 

 

First we verify we got the proper number of command line arguments, declare our various SFML and JSON related variables.  We are setting the ip address to 127.0.0.1, which is the loopback address, or the equivalent of saying “this machine”.  Next we build up our JSON string.  If you have worked with XML before, the process will be very familiar. We create a JSONObject named data, which is essentially a map of key value pairs of other JSONValues.  When then populate it with our data, then in turn use it as the parameter in creating a new JSONValue.  All the heavy lifting is done in JSONValue’s constructor.  Stringify() is the method that does the actual re-encoding returning a std::wstring.  Of course, we actually want a std:string, so we create one.  Obviously in time sensitive code, we would alter SimpleJSON to use std::string instead.  Our end result is a JSON string that looks like this:

 

{"action":"AddScore","name":"Bob Dole","score":23}

 

 

Now that we have our data in JSON encoded string format, it’s time to send it.  We simply append our string data to our packet and send it using our Socket.  If an error occurs, report it.  Otherwise, we are done, close our Socket and exit.  If you strip away all the wide character string annoyances, the process is actually quite straight forward.

 

 

Now lets take a look at the Node side of things.  The code is fairly long, so instead of walking through it I have simply commented it.  If you have any questions not covered by the comments, fire away.  So here is the contents of Server.js

 

var dgram = require('dgram'), fileSystem = require('fs'), highScores, server; //Load high scores from file fileSystem.readFile("HighScores.txt", 'utf8', function(err,data){ if(err){ //Error occurred loading file, spit out error message then die console.log("Error occurred loading file"); process.exit(); } console.log("Loading high scores from file"); try{ // use JSON to turn file contents back into a Javascript array object highScores = JSON.parse(data); }catch(e) { // Exception occurred parsing file contents as JSON, error out and die. console.log("Exception occured parsing data"); process.exit(); } // Now sort the high scores by score, high to low highScores.Scores.sort(function(a,b){ return b.Score - a.Score; }); // Display the sorted high scores to the console console.log(highScores); }); //Alternative way to read file in NodeJS //file.on("error",function(exception){ // process.exit(); // } //); //file.on("data",function(data){ // fileData = data; // } //); //file.on("close",function(){ // highScores = JSON.parse(fileData); //}); //Create a UDP socket server = dgram.createSocket('udp4'); console.log("Socket created"); // Add a handler for incoming traffic on the socket. This will be called each time something connects to the socket server.on("message",function (msg,rinfo) { //console.log(parseInt(msg).toString()); console.log(rinfo); // SFML sends two packets, one with the size of the following packet ( as a 4 byte number ) // We don't need it, so as a crude-hack, we ignore any 4 byte packets if(rinfo.size != 4) { console.log("Received message:" + msg.toString()); // Socket data comes in as a JSON encoded array of objects, turn back into a JS object var jsonData,i; try{ jsonData = JSON.parse(msg); } catch( exception ) { console.log("Invalid JSON request received"); return; // Non lethal error, just stop processing packet } // The action parameter determines what you should do with this packet switch(jsonData.action) { // action==AddScore, add the score to the highscore array if it's higher than an existing score case "AddScore": console.log("AddScore called\n"); // Make sure highscore has been initialize... order can be a weird thing in node if(highScores != undefined){ // Loop through current highScores ( which should be sorted ) // and insert score if a lower match found for(i=0;i < highScores.Scores.length;++i) { if(highScores.Scores[i].Score < jsonData.score){ highScores.Scores.splice(i,0,{"Name" : jsonData.name, "Score" : jsonData.score}); console.log("Inserted highscore by: " + jsonData.name); break; // match found, stop looping } } } // Display newly created highscore array console.log(highScores.Scores); break; } } // // }); // Called when socket starts listening for packets. besides logging, currently serves no purpose server.on("listening", function () { var address = server.address(); console.log("server listening " + address.address + ":" + address.port); }); // Finally, bind the server to port 1000. 1000 was randomly chosen. Think of this as saying GO! // Now we are listening for UDP connections on port 1000 server.bind(1000);

Click here to download server.js

 

 

Now start the server at the command line ( node server.js ) and run the client from a different command line.  It should look like this:

 

image

 

 

As you can see, we are successfully sending data from SFML over a socket to our node based server.  In the next part, we will look at sending data the other way.

 

 

You can download the complete project right here.  The scripts are in a sub-folder named NodeJS.

 

 

Click for part 3

Programming ,




Enough is enough Daz!

24. February 2012

 

 

I appreciate that you gave away your software for free and some of it has real potential, but the volume of spam is getting way too high. 

 

image

 

These are some of the emails I have received from DAZ since signing up earlier this month, this doesn’t include my receipts/serial number emails, nor the half dozen or so I deleted outright.  Even worse, the opt-out link results in:

 

image

 

 

 

 

Bit of advice if anyone from Daz is listening… dial the volume back and make opting out a one click process ( that works! ). General rule of business; don’t piss off your customers!

 

You have now been marked as junk by me and I imagine I am not alone.  Keep this up and all of your emails will be flagged as junk by default, completely ruining what you are trying to accomplish.  What about the rest of you that signed up for the free Daz/Bryce/Hexagon bundle, are you getting sick of the spam?

General




CryEngine 3.3.9 released

24. February 2012

 

 

CryTek’s freely available CryEngine has just had an update released.

Cryengine3

 

 

 

Easily the biggest new feature is integration of Autodesk Scaleform, a Flash based UI solution, that was also recently integrated in the competing  Unity engine.  In addition to Scaleform support, they announced to following new features:

 

 

In addition to offering Scaleform technology, the Free SDK update will also feature several DirectX 9 rendering improvements, providing POM (Parallax Occlusion Mapping), SSDO (Screen-space Directional Occlusion), Particle GI (Global Illumination) and a new “Very High” graphics configuration to support these new features and offer higher levels of rendering quality.

 

The CryENGINE team has also been working to provide fixes for the most prominent issues holding up the community. CryENGINE 3 Free SDK 3.3.9 includes improved SLI/CF support, improved 32-bit driver support, support for 5.1/7.1 surround sound systems and many improvements to CryENGINE 3 Sandbox.

 

You can download it here.

News




Photoshop Touch for Android update

17. February 2012

 

You may have read my review of Photoshop Touch for Android earlier.  If not, basicallyAdobe-Photoshop-Touch1 Photoshop Touch had all the makings of a very affordable while very capable Photoshop alternative for tablet owners.  That said, it was hobbled to the point of near uselessness by a couple really bad design decisions.

 

 

I happy happy to say, those mistakes have been rectified.  I have added a section to my review which covers the changes that have happened post release, which you can read here.

 

 

To put it simply, if you have an Android tablet, and are looking for an affordable and streamlined version of the Photoshop + Wacom tablet experience on a budget, you owe it to yourself to check out Photoshop Touch.  Could be the best 10 bucks you’ve ever spent, well say 20 bucks, once you toss in a stylus




Blender 2.62 released today

16. February 2012

 

Blender 2.62 was released today.  The 2.6 series of releases is all about adding in the variousbl branches that have been in development recently and this one is no exception.  Key new features include:

 

  • UV Tools, a number of new features have been added including an interactive stitch tool, sub-division surface aware UV unwrapping and a sculpting tool for selecting and tweaking UVs
  • Boolean library now using “Carve” library which should be faster, more stable with better overall results.  To the end user, other then improved results, there should be little difference to the existing interface
  • Cycles render engine ( render via GPU ) has had a number of new features including render layers/passes, multiGPU rendering and more
  • Object tracking support has been added
  • Remesh modifier, creates a new topology based on an input surface.  If I am honest, I’m not really sure what purpose you would use it for
  • Many bug fixes and other new features

 

For full details, you can go here with the bug fixes listed here.  To download the newest release head on over here. Have some patience though, as always with every new release days, their servers are getting hammered.

 

 

The next release (in April) is the one I am really waiting for, as it’s the one that finally adds BMesh support!  There is also a new team focusing on improving COLLADA support.  The future is looking extremely good!

 

Nice work Blender team, keep ‘em coming!

Art, News