Life Update and Gamer Footprint Update

7. August 2014 09:03 by Cameron in   //  Tags: , , , , , , , ,   //   Comments

Hey guys, I know it's been a while since I've written a blog post. I've been busy with wedding planning and other life events! I'm still alive and kicking!

Gamer Footprint (GFP) continues development in the spare time that I have. It's making slow but sure progress. I've moved Gamer Footprint to use a single page application (SPA) approach. The previous iterations of GFP were based on strict ASP.NET MVC, but as of late, I've been on a single page application kick. :) The advantages to SPAs are excellent over traditional multi-page applications. With SPAs, you can load your application shell and then load/post content on demand. This gives a perceived faster and more responsive application. 

With GFP, I've been playing around with Web API 2 and Durandal. Web API 2 is Microsoft's take on RESTful API development. It's very robust and supports all types of HTTP calls as defined by the REST design principles. By using Durandal, I can use RequireJS, Knockout.js, and jQuery to build rich single page applications. This proves to be a good choice for the development of GFP because I already have experience in RequireJS, Knockout, and jQuery. If I had chosen to use AngularJS, it would have required to learn a completely new framework. While some might not worry about that, I've already been using Knockout in some projects at my day job, so naturally, it made sense to learn Durandal.

Visual Studio Update 3 and TypeScript external module compilation

7. August 2014 08:54 by Cameron in TypeScript  //  Tags: , , , , , , , ,   //   Comments

I recently updated to Visual Studio 2013 Update 3, but it appears as though the TypeScript compiler is a little more strict in external module compilation. If you receive an error message asking the --module flag to be set, you need to add this to your build configurations in your project:

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <TypeScriptModuleKind>amd</TypeScriptModuleKind>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <TypeScriptModuleKind>amd</TypeScriptModuleKind>
  </PropertyGroup>

After adding the TypeScriptModule kind node to the project properties, the compiler can correctly compile external TypeScript modules!

Disqus now in effect for comments!

2. May 2014 14:02 by Cameron in Blog  //  Tags:   //   Comments

I don't know why it took so long to make the move to Disqus for the comment system, but I'm glad I did. There is one caveat though: all previous comments are now hidden. Moving forward, we will have much more flexibility for comments and hopefully less spam. You wouldn't believe how much spam I was getting with the standard comments system. Some spam bots were so badly written that they had malformed anchor tags embedded in their comments. It's extremely clear when comments are spam or when they're not.

Knockout Observable Array Performance Improvments

Today, I was working on a project that makes AJAX calls to an API and noticed that the application was very slow and unresponsive during the AJAX calls and ko updates. I checked that the requests weren't taking too long in Chrome's developer tools and found they were at max taking 300ms and minimum of 20ms to return. This was very odd behavior since the AJAX requests weren't blocking.

After some research, I found that when updating ko.observableArrays, you don't want to push each individual item to the array if you can help it. Each time an item is pushed to a ko.observableArray, it notifies its subscribers and depending on how many DOM elements you have on your page, that can be rather taxing. I had code like this:

$.ajax({
	url: 'api/Document/',
	method: 'GET',
	dataType: 'json',
	success: function(data) {
		Documents([])
		for(var i = 0; i < data.length; i++)
			Documents.push(data[i]);
	}
});

To fix this, you need to create temporary arrays to push your items to and then assign the ko.observableArray to the contents of the temporary array. Your code should look more like this:

$.ajax({
	url: 'api/Document/',
	method: 'GET',
	dataType: 'json',
	success: function(data) {
		var documents = [];
		for(var i = 0; i < data.length; i++)
			documents.push(data[i]);	

		Documents(documents);
	}
});

Gamer Footprint Update

4. March 2014 11:27 by Cameron in Gamer Footprint, Programming  //  Tags: , ,   //   Comments

Some of you may be wondering what is the current status of Gamer Footprint? Development is live and well! In fact, there is a slightly functional proof of concept available to test at https://www.gamerfootprint.com. I have also bought the domain www.gamrfp.com but this domain doesn't have any SSL configured yet. I mainly bought it for a shorter domain.

Please do note that the website is rapidly changing with new features several times a week and I sometimes have to clear out the database when my domain model changes or I find bugs that cause duplicate data. Therefore, the site is not 100% stable yet. I'm working very hard on making the user experience enjoyable. Some features that have been implemented are:

  • User profiles
    • The profiles are very basic right now, but I plan to implement a full timeline-like profile that includes a gamer's events such as played games or achievements/trophies
    • Your avatar is currently pulled from Gravatar and if you don't have a Gravatar, you should get one! It's a very nice service to have the same avatar across multiple websites. If no Gravatar can be found for your account email, it will display a generic image in place of your avatar.
    • If you have entered a PlayStation Network and/or an Xbox LIVE account, your profile will show your current presence on PlayStation Network and Xbox LIVE
    • Your information is safe and secured and transmitted over SSL. You only have to login once for your PSN or Xbox LIVE accounts. We don't store any passwords for accounts on our servers. I'm working on providing a method for not requiring account credentials, but you won't be able to send messages. If messaging your Xbox LIVE or PSN friends from a PC is important, you'll need to enter your account credentials.
  • Xbox LIVE games and achievements (Xbox One games and challenges support to come soon!)
  • Data APIs that directly interface with Xbox LIVE, PlayStation Network, and Steam
    • Get Xbox and Xbox One games, achievements, challenges, friends, presence and profile information
    • Get PlayStation games, trophies, friends, presence information and profile information - supports PSVITA, PS3, and PS4
    • These APIs have not yet been made public, but they should be available very soon. I will post documentation on how to request an API key and API endpoints for PSN and Xbox LIVE. I will not provide a data API for Steam since Valve provides their own and I don't want to use up my daily requests to the API with a wrapper API. There might be a way to let developers aggregate everything from all supported game networks (including Steam), but it would involve providing individual Valve issued API keys and I don't want to do that as it might be against their terms of service.
Planned features:
  • PlayStation Network games and trophies
    • The core for interfacing with PSN has been finished, but I have yet to write the data access layer and data persistence layer for this part
  • Timeline system - user level and global level
  • Message friends on Xbox LIVE and PSN from the website
  • Invite people via Xbox LIVE or PSN message
  • and much more!
Here are some preview screenshots:
 

Windows Path Environment Variable Length

10. February 2014 13:14 by Cameron in Windows  //  Tags: , , , , , ,   //   Comments

I recently started getting "Target Invocation" errors while trying to launch Visual Studio 2012 or Visual Studio 2013. This was a strange error message as not much else was given to diagnose the problem. After some extended research, I found that having an extra long PATH variable can cause Visual Studio to hang on startup. Windows limits this length to 2048 characters. Although you can still have a PATH variable longer than that, some programs like Visual Studio can't address a PATH environment variable larger than 2048 characters. A remedy to this problem is to split out your PATH variable into at least two variables. 

First, you need to open your PATH variable and copy its value into a safe spot in case it gets messed up. Then you can extract the last half of your path and create a new environment variable and call it PATH2. After your new environment variable is setup, you can reference it in your main PATH

C:\bin;C:\anotherfolder\bin;%PATH2%

Some may be asking, why would my PATH ever exceed 2048 characters? The answer is that if you're a developer and have lots of compilers, IDEs, and toolchains installed on your development machine such as Visual Studio, Net Beans, GCC, Qt, or devkitpro, your PATH can grow fairly quickly. 

If you ever exceed 2048 characters in PATH2, you can append another variable such as PATH3 or PATH4. I'm not certain if there is a real physical limit to user created environment variables, but if you experience problems, you can do as I mentioned. Note, I have a Windows 7 x64 development machine at work and I haven't seen this issue in Windows XP or Windows 8. I haven't done development on Windows XP in several years since it is now showing its age and Microsoft is discontinuing support in April 2014.

On-the-Fly Windows Impersonation with C#

6. February 2014 09:37 by Cameron in Programming  //  Tags:   //   Comments

Occasionally I need to perform tasks under another user so I created a nice wrapper class that allows me to impersonate as another user on the fly:

using System;

using System.Runtime.InteropServices;

using System.Security.Permissions;

using System.Security.Principal;

using Microsoft.Win32.SafeHandles;

using System.Runtime.ConstrainedExecution;

using System.Security;

using System.Configuration;



namespace Tinksoft.Tools

{

    /// <summary>

    /// Facilitates impersonation of a Windows User.

    /// </summary>

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]

    public class Impersonation : IDisposable

    {

        public string Environment { get; set; }



        public string UserName { get; set; }



        public string Password { get; set; }



        public string DomainName { get; set; }



        public enum LogonType

        {

            Interactive = 2,

            Network = 3,

            Batch = 4,

            Service = 5,

            Unlock = 7,

            NetworkClearText = 8,

            NewCredentials = 9

        }



        public enum LogonProvider

        {

            Default = 0,

            WinNT35 = 1,

            WinNT40 = 2,

            WinNT50 = 3

        }



        /// <summary>

        /// Windows Token.

        /// </summary>

        private readonly SafeTokenHandle _handle;



        /// <summary>

        /// The impersonated User.

        /// </summary>

        private WindowsImpersonationContext impersonatedUser;



        public Impersonation()

        {

        }



        /// <summary>

        /// Initializes a new instance of the Impersonation class. Provides domain, user name, and password for impersonation.

        /// </summary>

        /// <param name="domainName">Domain name of the impersonated user.</param>

        /// <param name="userName">Name of the impersonated user.</param>

        /// <param name="password">Password of the impersonated user.</param>

        /// <remarks>

        /// Uses the unmanaged LogonUser function to get the user token for

        /// the specified user, domain, and password.

        /// </remarks>

        public Impersonation(AccountCredentials credentials)

        {            

            string[] splitName = WindowsIdentity.GetCurrent().Name.Split('\\');

            string name = (splitName.Length > 0) ? splitName[0] : null;



            LogonType logonType = LogonType.Interactive;

            LogonProvider logonProvider = LogonProvider.Default;



            if (name != credentials.Domain)

            {

                logonType = LogonType.NewCredentials;

                logonProvider = LogonProvider.WinNT50;

            }



            // Call LogonUser to obtain a handle to an access token.

            bool returnValue = LogonUser(

                                credentials.UserName,

                                credentials.Domain,

                                credentials.Password,

                                (int)logonType,

                                (int)logonProvider,

                                out this._handle);



            if (false == returnValue)

            {

                // Something went wrong.

                int ret = Marshal.GetLastWin32Error();

                throw new System.ComponentModel.Win32Exception(ret);

            }



            this.impersonatedUser = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());    

        }



        /// <summary>

        /// Initializes a new instance of the Impersonation class. Provide domain, user name, and password for impersonation.

        /// </summary>

        /// <param name="domainName">Domain name of the impersonated user.</param>

        /// <param name="userName">Name of the impersonated user.</param>

        /// <param name="password">Password of the impersonated user.</param>

        /// <remarks>

        /// Uses the unmanaged LogonUser function to get the user token for

        /// the specified user, domain, and password.

        /// </remarks>

        public Impersonation(string domainName, string userName, string password)

        {

            string[] splitName = WindowsIdentity.GetCurrent().Name.Split('\\');

            string name = (splitName.Length > 0) ? splitName[0] : null;



            LogonType logonType = LogonType.Interactive;

            LogonProvider logonProvider = LogonProvider.Default;



            if (name != domainName)

            {

                logonType = LogonType.NewCredentials;

                logonProvider = LogonProvider.WinNT50;

            }



            // Call LogonUser to obtain a handle to an access token.

            bool returnValue = LogonUser(

                                userName,

                                domainName,

                                password,

                                (int)logonType,

                                (int)logonProvider,

                                out this._handle);



            if (false == returnValue)

            {

                // Something went wrong.

                int ret = Marshal.GetLastWin32Error();

                throw new System.ComponentModel.Win32Exception(ret);

            }



            this.impersonatedUser = WindowsIdentity.Impersonate(this._handle.DangerousGetHandle());

        }



        [DllImport("advapi32.dll", SetLastError = true)]

        private static extern bool LogonUser(

                string lpszUsername,

                string lpszDomain,

                string lpszPassword,

                int dwLogonType,

                int dwLogonProvider,

                out SafeTokenHandle phToken);



        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]

        private static extern bool CloseHandle(IntPtr handle);



        public void Dispose()

        {

            this.impersonatedUser.Dispose();

            this._handle.Dispose();

        }



        private static string[] GetAccountInfo(string accountInfo)

        {

            return accountInfo.Split(' ');

        }

    }



    public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid

    {

        private SafeTokenHandle()

            : base(true) { }



        [DllImport("kernel32.dll")]

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]

        [SuppressUnmanagedCodeSecurity]

        [return: MarshalAs(UnmanagedType.Bool)]

        private static extern bool CloseHandle(IntPtr handle);



        protected override bool ReleaseHandle()

        {

            return CloseHandle(handle);

        }

    }

}
 

Happy New Year 2014!

6. January 2014 22:48 by Cameron in Programming  //  Tags:   //   Comments

I know I'm a few days late for posting on New Year's day, but I was away in a cabin in northern California with limited Internet. 2014 is a new year and I plan to write at least one blog post a month. Last year, I didn't write a post each month, but I don't want to become uninterested in blog writing and learning new technologies. The best action a developer can take for his/her career is learning new skills - constantly. The world of technology is forever changing and evolving and those who sit complacently will fall behind and lose their competitive edge. Each year, as new students graduate college, they will be sharper and brighter with the newest tech in the industry. Anyone who fails to embrace new technologies will not survive in the coming wave of millenials entering the workforce, eventually replacing the vast majority of the workforce. People who fail to think about the future will sadly not have a future in IT. If you're in your mid to late 30's and haven't picked up a new skill lately, it's time to do so. Time is of the essence and the future of IT lies in the hearts and minds of the millenials and their children.

HDMI Audio Kext for NVIDIA Graphics - Hackintosh

6. January 2014 22:40 by Cameron in   //  Tags: , , , , , ,   //   Comments

I've been searching for HDMI audio for quite some time for my desktop hackintosh and tonight I found just the right thing! Simply install the kext by running the install.sh script from the dmg image and voila! I have tested my NVIDIA GTX 660 on OS X Mavericks 10.9.1 and it works beautifully! I tried the DSDT editing but I don't think I got it quite right and it wasn't detecting my TV as an audio output device. After I installed the kext, all was good! Note that all volume control must be done through your TV or audio receiver. OS X doesn't support volume control on digital audio devices. Also, I'm not sure if you can use this kext in conjunction with AppleHDAEnabler, but I don't see why this would be a problem. TonyMacx86 should include this in Multibeast as it would make life much easier for those who don't want to mess with their DSDT.

NVAudio-1.0.dmg (27.02 kb)

Compile kernel for Allwinner A20 Android Jelly Bean 4.2.2 tablet

12. December 2013 10:33 by Cameron in Android  //  Tags: , , , , , , ,   //   Comments

I recently bought an iView 777TPCII 7" Allwinner A20 tablet running Android Jelly Bean 4.2.2 and it has USB OTG support, but sadly no native joystick support! So naturally, I thought, I need to compile the necessary kernel modules to enable joysticks/gamepads. I'm no expert on compiling kernels for Android, but this method seems to work. You need to have a Linux or cygwin environment to compile your kernel. I've used VirtualBox on Windows 7 and installed Ubuntu 13.10 32-bit to setup my Linux build environment. I'm assuming you can install Linux in a virtual machine. It's fairly straight forward. After you have installed Ubuntu, you need to install the toolchain for building the kernel:

sudo apt-get install gcc-arm-linux-gnueabihf build-essential git u-boot-tools libncurses5-dev

Then you need to clone the linux-sunxi repository so you can build it:

git clone -b sunxi-3.4 https://github.com/linux-sunxi/linux-sunxi.git

After you have cloned the repository, change to the linux-sunxi directoy:

cd linux-sunxi

Then you need to setup the configuration for the A20 using this command:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun7i_defconfig

Now you can configure your kernel options by running:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

Proceed to building your kernel and modules

make -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage modules

The -j4 option runs 4 make instances at the same time. Adjust to your CPUs available.

Finally, you need to create a full module tree:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=output modules_install

Month List

Tag cloud