WinRT API and Unity 5

So I needed access to WinRT APIs in Unity.  (I am only targeting Windows platforms – this will NOT work cross platform.)  Unity docs say you can use WinRT from within Unity (http://docs.unity3d.com/Manual/windowsstore-scripts.html) however the issue I ran into is Unity only supports .NET 2.0 and many of the WinRT calls force you to use async and await, which are only available in .NET 4.0.
So the next most logical thing to do was to create a native plugin.  But while I was exploring this options I came across a XAML to Unity connection example (http://docs.unity3d.com/Manual/windowsstore-examples.html).  Turns out this little example project shows how to wire up a communication bridge between your Windows Store App and Unity.  Just what I needed!

First in Unity add a new script called XAMLConnection to your Camera.

using UnityEngine;
using System.Collections;

public class XAMLConnection : MonoBehaviour
{	
	public delegate void OnEvent(object arg);
	
	public OnEvent onEvent = null;
	
	void Start () 
	{
		
	}
	
	void Update () 
	{
		
	}

Here are the steps to wire up your own Windows Store App to Unity bridge.

  1. Build your Unity Project.  Be sure to target the Windows Store build target and set the SDK to 8.1.
  2. You will be prompted for a target build folder.  Create a new folder called Windows8_1.
  3. Once the build is complete browse to your newly created Windows8_1 project and open the Visual Studio Solution file (the .sln file).
  4. Build and run the project in Visual Studio to verify you don’t have any issue before proceeding.  Would hate for you to think there was something wrong with this tutorial when it is really just some rotten code in your game 😉
  5. Now that we have verified the default build worked correctly we can begin modifying the app.  Open the MainPage.xaml.cs file

In the constructor add the following line:

UnityPlayer.AppCallbacks.Instance.Initialized += OnInitialized;

then add the OnInitalized, UnityToXAML and XAMLToUnity methods to the MainPage class

        private void OnInitialized()
        {
            Communications.SetEvent(UnityToXAML);
        }
        public void UnityToXAML(object arg)
        {
            UnityPlayer.AppCallbacks.Instance.InvokeOnUIThread(new UnityPlayer.AppCallbackItem(() =>
            {
                // Windows Store App code here               
            }
            ), false);
        }
       
        private void XAMLToUnity(object sender, RangeBaseValueChangedEventArgs e)
        {
            if (UnityPlayer.AppCallbacks.Instance.IsInitialized())
            {
                UnityPlayer.AppCallbacks.Instance.InvokeOnAppThread(new UnityPlayer.AppCallbackItem(() =>
                {
                    //Unity code goes here ...
                    UnityEngine.GameObject go = UnityEngine.GameObject.Find("Camera");                    
                }
                ), false);
            }
        }

Now we add the delegate and class that link the Windows Store App and Unity together. The example has this in the MainPage.xaml.cs file – but out side of the MainPage class. The delegate and the Communications class are within the namespace but not in the MainPage class.

    public delegate void UnityEvent(object arg);
    public sealed class Communications
    {
        public static void SetEvent(UnityEvent e)
        {

             UnityPlayer.AppCallbacks.Instance.InvokeOnAppThread(new UnityPlayer.AppCallbackItem(() => 
                 {
                    UnityEngine.GameObject go = UnityEngine.GameObject.Find("Camera");
                    if (go != null)
                    {
                        go.GetComponent().onEvent = new XAMLConnection.OnEvent(e);
                    }
                    else
                    {
                        throw new Exception("Cube not found, have exported the correct scene?");
                    }
                 }), true);            
        }

Now run your project in Visual Studio. You now have a bridge between Unity and Windows Store Apps.

Streaming JSON parser

So I found something I don’t like about nodejs.  It has a low memory limit.  The limit is around 2 GB of memory ; even on a 64 bit system.  It is a V8 thing.  If you don’t believe me try parsing a json file > 1GB – not going to work.  You end up with a nice out of memory exception.

So how do you parse a very large json file?  The same way you do everything else in NodeJS – async.  You stream the file and wait for events.  The jsonparse (https://github.com/creationix/jsonparse/) module is a huge help!


var Parser = require('jsonparse');

var fs = require('fs');

var p = new Parser();

p.onValue = function (value) {
 if(value.StatementNumber && value.StatementNumber == "12345"){
 console.log("Found me");
 }
};

var stream = fs.createReadStream('data.json', {});
stream.on('data',function(buffer){
 p.write(buffer);
});

NodeJS / Express on Azure Web Sites with a Azure Blob Session Store

Getting NodeJS and Express running on Azure Web Sites is really easy.  The Node/Express/Azure community is fantastic and provides wonderful guidance.  However, I ran into a few challenges and I wanted to document them here just in case it helps anyone else.

Express Sessions

Out of the box Express gives you in memory sessions.  When you scale your sites in Azure (love that feature) in memory sessions don’t work so great anymore.  I needed a central place to store the sessions.  Azure Cache or Redis makes a fast and expensive session provider.  I really wanted something that would be very cheap.  So I choose Azure Blob Storage and needed to write a customer provider.

The connect session documentation was a huge help and I used TJ Holowaychuk’s implementation of a Redis session store as a guide.  To get started I created an object with three functions get, set, and destroy.  Each function implements a call to Azure Blob Storage for basic CRUD operations for the session object.


//Implemented from https://github.com/expressjs/session
//                 https://github.com/tj/connect-redis

var azure = require("azure-storage");

function azureSessionStoreFactory(session) {
    var Store = session.Store;
    function AzureSessionStore(options) {
        this.config = options;        
        this.blobService = azure.createBlobService(options.storageAccountKey);
        options = options || {};
        Store.call(this, options);
    }
    AzureSessionStore.prototype.__proto__ = Store.prototype
    
    var azureSessionPrototype = AzureSessionStore.prototype;
    azureSessionPrototype.get = function (sid, callback) {
        var self = this;
        self.blobService.getBlobToText('websessions', sid, function (err, result) {
            if (err) {
                callback(err, null);
            } 
            else {
                callback(null, JSON.parse(result));
            }
        });
    }
    
    azureSessionPrototype.set = function (sid, session, callback) {
        var self = this;
        self.blobService.createBlockBlobFromText('websessions', sid, JSON.stringify(session), function (err, results) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, session);
            }
        });
    }
    
    azureSessionPrototype.destroy = function (sid, callback) {
        var self = this;
        self.blobService.deleteBlobIfExists('websessions', sid , function (err) {
            if (err) {
                callbak(err, null);
            }
            else {
                callbak();
            }
        });
    }

    return AzureSessionStore;
}

module.exports = azureSessionStoreFactory;

To use our new Azure Blob session store we can plug it into Express with the following code.

</pre>
var app = express();
var sessionStore = new sessionStorage({ storageAccountKey: cloudStorage.primaryStorageAccount });

app.use(session({
store: sessionStore,
secret: '5up3r s3cret p@ssw0rd',
resave : true,
saveUninitialized : true
}));

While this is not the fastest session store.  It is a whole lot cheaper than Azure Cache or hosting Redis on a Virtual Machine.

Check out the code on GitHub https://github.com/rocketcoder/connect-session-azure-blob

Getting NodeJS/Express on Azure Web Sites to only serve https

Azure has done a fantastic job of making it easy to deploy a NodeJS application to Azure Web Sites.  However, one thing that took a bit of searching to find is how to force Azure Web Sites to only serve https requests.  Fortunately this is really easy.  I found the answer on Scott Hanselman’s blog but I am also posting it here.

Add the following to you Web.config.  (In Azure Web Sites nodejs is hosted behind IIS and the web.config controls how IIS behaves.)

    <rewrite>
      <rules>
        <clear />
        <rule name="Redirect to https">
          <match url="(.*)"/>
          <conditions>
            <add input="{HTTPS}" pattern="Off"/>
            <add input="{REQUEST_METHOD}" pattern="^get$|^head$|^post$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"/>
        </rule>
        <rule name="app" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="iisnode.+" negate="true" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
          <action type="Rewrite" url="bin\www" />
        </rule>
      </rules>
    </rewrite>

NodeJS / Express / PassportJS building a local security strategy

Getting security setup in Express with PassportJS had a slight learning curve to it for me.  PassportJS has a variety of strategies that they use to implement security.  I needed a custom security provider based on Azure Table Storage.  Azure Table Storage is cheap and lightweight, so it was a great fit for a developer on a budget.  With Passport the key object to implement is the User object.  User implements a few key functions findOne, save, and isValidPassword.  I also used the bcrypt package to encrypt the passwords.  If you are looking for a more indepth tutorial on how to use PassportJS check out http://scotch.io/tutorials/javascript/easy-node-authentication-setup-and-local

var azure = require("azure-storage");
var q = require('q');
var config = require('../config.js');
var bCrypt = require('bcrypt-nodejs');

function User() { }
   
User.UserFactory = function() {
    var user = {
        userName : "",
        password : "",
        enabled : true,
        email : "",
        validated : false
    };
    user.isValidPassword = User.isValidPassword;
    return user;
}
   

User.findOne = function (userName) {
    var tableService = azure.createTableService(config.storageAccountKey);
    var deferred = q.defer();   
    tableService.retrieveEntity('CloudUser', userName, 'true', function (err, result) {
        if (err) {
            if (err.statusCode === 404)
                deferred.resolve(undefined);
            else
                deferred.reject(err);
        }
        else {
            deferred.resolve(User.toUser(result));               
        }
    });
    return deferred.promise;
}
 
User.toUser = function (userEntity) {
    var user = User.UserFactory();
    user.userName = userEntity.PartitionKey._;
    user.enabled = userEntity.RowKey._;
    user.password = userEntity.password._;
    user.email = userEntity.email._;
    user.validated = userEntity.validated._;
    return user;
};
   
User.toUserEntity = function (user) {
    return {
        PartitionKey: { '_': user.userName },
        RowKey: { '_': 'true' },
        password: { '_': user.password },
        email: { '_': user.email },
        validated: { '_': user.validated }
    };
};
   
User.createUser = function (user) {
    var deferred = q.defer();
    var tableService = azure.createTableService(config.storageAccountKey);
    function create(user) {                       
        tableService.insertEntity('CloudUser', User.toUserEntity(user), function (err, result) {
            if (err) {
                deferred.reject({ status: false, reason: &quot;error creating user&quot;, err: err });
            }
            else {
                deferred.resolve({ status: true, reason: &quot;&quot; });
            }
        });
    }

    User.findOne(user.userName).then(function (result) {
        if (result && result.length > 1) {
            deferred.resolve({ status: false, reason: &quot;username exists&quot; });
        }
        else {
            create(user);
        }
    }).
    fail(function (err) {
            deferred.reject({ status: false, reason: "error creating user";, err: err});
    });

    return deferred.promise;
}
   
User.save = function (user) {
    var tableService = azure.createTableService(config.storageAccountKey);
    tableService.updateEntity('CloudUser', user, function (err, result) {
        if (err) {
            deferred.reject({ status: false, reason: "error creating user" });
        }
        else {
            deferred.resolve({ status: true, reason: "" });
        }
    });
}
   
User.isValidPassword = function (user, password) {
    return bCrypt.compareSync(password, user.password);
}
   
// Generates hash using bCrypt
User.createHash = function (password) {
    return bCrypt.hashSync(password);
}

module.exports = User;

Now that we a User object that can find, save and validate passwords – we need plug it into a local strategy.

module.exports = function (passport) {

passport.use('login', new LocalStrategy({
passReqToCallback : true
},
function (req, username, password, done) {
// check if a user with username exists or not
User.findOne(username).then(function(user){
if (!user) {
console.log('User Not Found with username ' + username);
return done(null, false, req.flash('message', 'User Not found.'));
}
// User exists but wrong password, log the error
if (!User.isValidPassword(user, password)) {
console.log('Invalid Password');
return done(null, false, req.flash('message', 'Invalid Password')); // redirect back to login page
}
// User and password both match, return user from done method
// which will be treated like success
return done(null, user);
}).fail(function (err) {
// In case of any error, return using the done method
return done(err);
});

})
);
}

Of course we will also need a strategy to sign up new users.

&lt;/pre&gt;
module.exports = function (passport) {
passport.use('signup', new LocalStrategy({
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function (req, userName, password, done) {

findOrCreateUser = function () {
// find a user in Mongo with provided username
User.findOne(userName).then(function (user) {
// already exists
if (user) {
console.log('User already exists with username: ' + username);
return done(null, false, req.flash('message', 'User Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = User.UserFactory();

// set the user's local credentials
newUser.userName = userName;
newUser.password = User.createHash(password);
newUser.email = req.param('email');
newUser.firstName = req.param('firstName');
newUser.lastName = req.param('lastName');
// save the user
User.createUser(newUser).then(function (err) {
console.log('User Registration succesful');
return done(null, newUser);
}).fail(function (err) {
if (err) {
console.log('Error in Saving user: ' + err);
throw err;
}
});
}
}).fail(function (err) {
// In case of any error, return using the done method
if (err) {
console.log('Error in SignUp: ' + err);
return done(err);
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
}

And finally plug the strategies into Passport with the following code.


// Initialize Passport
app.use(passport.initialize());
app.use(passport.session());

// Passport needs to be able to serialize and deserialize users to support persistent login sessions
passport.serializeUser(function (user, done) {
done(null, user.userName);
});

passport.deserializeUser(function (id, done) {
User.findOne(id).then(function (user) {
done(null, user);
}).fail(function (err) {
done(err);
});
});

// Setting up Passport Strategies for Login and SignUp/Registration
login(passport);
signup(passport);

You can checkout the code at https://github.com/rocketcoder/passportjs-AzureTableStorge