Recently, I was working on some editor tools for Unity that required database access and a simple web frontend to add, delete, and modify data in the database. This is possible with a MySQL backend and some sort of frontend web page. But considering the workflow for the team, I knew that I didn’t have enough time to make the frontend pretty and very usable. My team would have to learn a different interface/tool to modify the data in the database. It would probably have bugs. And what if I needed to give access to someone outside the company? Then my tool suddenly needs to deal with user authentication. On top of that, the database would need to be stored in a reliable network location for both internal and external use – what a mess.

But then it hit me! Maybe I could make use of Google’s availability, reliability, and excellent user interface to do exactly what I was looking for – which was basically to have an organized and secure collection of data be consumed by my game.

This post will explore the potential benefits of using Google’s architecture. Then, to get started, I’ll explain how I set up Google authentication to access spreadsheets, documents, etc. from Unity.

Benefits of Using Google Services

Google provides many services that could be useful in game development workflows. One great application is localization.  By using Google Doc’s spreadsheet APIs, localization is made much less complicated:

  • A Google spreadsheet can be edited simultaneously by many people.
  • If you need to share data with an external localization team, you can simply share the spreadsheet with their Google accounts.
  • Google already has excellent authentication features, which ensure that your data is only seen by those you allow.
  • By keeping all data in a single location, you greatly reduce the number of errors that are likely to occur.  In editor, your game can pull data from the spreadsheet.  When you actually create a build for publishing, you can generate XML files from the spreadsheet data.
  • The game engine can both read data from the spreadsheet and write data to the spreadsheet.  This means you can edit strings in the editor and code a mechanism for corrections or changes to be pushed to the spreadsheet.  Everything could always up to date.

Clearly there are a lot of benefits…except first you’ve got to set it up.  Let’s get to it!

Gain Access to Google APIs

The majority of Google’s APIs are free to use, but you need to sign up for the API Console to use them. After logging in with your Google account, you should get a prompt to create a new project.  Click the big blue button and Google will create a generic “project” that is used for API access:

A Google API Project!

You should use the drop down in the upper-left corner of the interface to rename your project to something appropriate for whatever you plan to use the API for. For this article, I just called it “UnityConnect”.

You can check out the “Services” tab to get an idea of what Google’s APIs can do for you.  However, for document and spreadsheet access, you actually don’t need to change the service settings at all.

Integrate Google’s SDK into Your Unity Project

“Google Data API SDK,” as they call it, is pretty easy to integrate into a Unity project.  Just download the appropriate package from http://code.google.com/p/google-gdata/downloads/list – the msi setup file for Windows and libgoogle tar file for Unix. I would recommend using the msi installer if at all possible, as it gives you the library in DLL form (which I believe works in both Windows and Mac installs of Unity).  Msi files are easy to deal with on Windows, and I think some Mac extractor tools can open msi files.

So, assuming you are using the MSI installer, the DLL files will be located at “C:\Program Files(x86)\Google\Google Data API SDK\Redist” after the install.  To use these DLLs in Unity, simply copy and paste them somewhere in your Assets folder.  I’d suggest a “Plugins” folder, as that adequately describes what the DLLs are.

The DLLs required depend on your needs. You can copy everything into your Unity folder for simplicity, but it might be good to copy them over piecemeal as you need them.  To access spreadsheets in Google Docs, you only need a few of the DLLs:

  • Google.GData.AccessControl
  • Google.GData.Client
  • Google.GData.Extensions
  • Google.GData.Spreadsheets
  • Newtonsoft.Json

Note that if you receive any sort of “TypeLoadException” or similar runtime errors, it is probably because you didn’t include a required DLL.  Again, it is totally possible to just include all the Google DLLs for simplicity’s sake, but if you are looking to optimize, you might be able to exclude the YouTube DLL, for example, if you aren’t using YouTube.

You should now be able to access these libraries in your scripts by adding a “using” directive:

using Google.GData.Spreadsheets;

Authentication

Finally, time for some coding. The API is actually very well documented: https://developers.google.com/google-apps/documents-list/.  I’d suggest reading through that a bit to get a better idea of how things work.

Before you can access a user’s data, you need to give them the opportunity to authenticate and say that it’s OK for your program to be snooping around and making changes. Google supports authentication through ClientLogin, OAuth1.0, and OAuth2.0 as of this writing.

I won’t recommend OAuth2.o over ClientLogin because, while it is more secure, it is also more cumbersome.  The method you choose will depend on the needs of your project and the time available to you.  It is worth mentioning, though, that Google highly recommends using OAuth2.0 for the most security.

Authentication with ClientLogin

ClientLogin is what you imagine when someone says authentication – just a username and password.  You can implement it like so:

using UnityEngine;
using System.Collections;
using Google.GData.Client;
using Google.GData.Spreadsheets;

public class GoogleSpreadsheet
{
    private SpreadsheetsService service;

    private void AuthenticateClientLogin(string pUsername, string pPassword)
    {
        // Create the service and set user credentials.
        service = new SpreadsheetsService("UnityConnect");
    	service.setUserCredentials(pUsername, pPassword);
    }
}

When creating the SpreadsheetsService, make sure the string passed in contains the name of the Google API Project you created earlier.  Then, just pass in the user credentials (either hardcoded or entered in some sort of Unity editor UI).  When you are reading or writing the spreadsheets later, you’ll use the SpreadsheetsService object to submit those read/write queries to Google.

Authentication with OAuth2.0

OAuth2.0 will require a bit more effort (both for you and your users), but it may be worthwhile if you want to provide more security than just a username/password combo.

Here is the class fragment above, but with OAuth instead:

using UnityEngine;
using System.Collections;
using Google.GData.Client;
using Google.GData.Spreadsheets;

public class GoogleSpreadsheet
{
    private SpreadsheetsService service;

    //*******************************************
    //* OAUTH VARIABLES
    //*******************************************
    private OAuth2Parameters oAuthParams;

    // OAuth2.0 info.
    private const string CLIENT_ID = "xxxxxxxxxxxx.apps.googleusercontent.com";
    private const string CLIENT_SECRET = "";
    private const string REDIRECT_URI = "";
    private const string SCOPE = "https://spreadsheets.google.com/feeds/ https://docs.google.com/feeds/";

    private void Authenticate()
    {
        // Set login info.
        service = new SpreadsheetsService("UnityConnect");

        // Create OAuth2 Parameters.
        oAuthParams = new OAuth2Parameters();
        oAuthParams.ClientId = CLIENT_ID;
        oAuthParams.ClientSecret = CLIENT_SECRET;
        oAuthParams.RedirectUri = REDIRECT_URI;
        oAuthParams.Scope = SCOPE;

        // Open web browser for user to authenticate.
        string authUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(oAuthParams);
        Application.OpenURL(authUrl);
    }

    public void FinishAuthenticate(string accessCode)
    {
        // Save access code.
        oAuthParams.AccessCode = accessCode;

        // Get access token.
        OAuthUtil.GetAccessToken(oAuthParams);
        string accessToken = oAuthParams.AccessToken;

	// Save data to make authorized requests.
        GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "UnityConnect", oAuthParams);
        service.RequestFactory = requestFactory;
    }
}

OAuth requires that you fill an OAuth2Parameters struct with data about the authorization you are requesting.  This data is mainly the following things: CLIENT_ID, CLIENT_SECRET, REDIRECT_URI.  The ID identifies the API project being used, while the SECRET ensures that it is OK to even be requesting authorization.  Keep the secret safe!

To fill in these values, you need to go back to the Google API Console.  In the sidebar on the left, select “API Access” and create a new OAuth2.0 client ID.  Be sure to identify your application as an Installed Application or else you won’t get all the information you need to complete OAuth setup.

Once you’ve generated an OAuth2.0 Client ID, copy and paste the needed info into your code:

My least favorite thing about OAuth is that it is a two step process. After starting to authenticate (around line 35 above), you’ll be given a URL generated by Google that the user must go to in their browser.  As a convenience, Unity provides the OpenURL function, which will open it for us.

In the browser, your user will see that you are requesting certain permissions, which are defined by the SCOPE variable. Once they accept your permissions, they will be given an access code that they must copy and paste into your application.  This means that you must provide an input field somewhere for this value.  It could be an editor field, a UI field in your game, an entry in a JSON or XML file, or a parameter in a prefab.  It just has to go somewhere.  It is actually possible to automate this process by using “localhost” as the redirect URI, then having a web server running on the local machine, but it is really far more complicated than you want this process to be.  See this documentation for more details if you go down that route.

After the user copies their access code in, they can finish the authentication, which is done by calling the FinishAuthenticate method above. Ultimately, like ClientLogin, this will culminate in the SpreadsheetsService being given credentials that allow it to query a user’s spreadsheets.

And Then Mono Made Things Difficult

Everything that we’ve written so far should work in normal .NET environments, but Mono has a particular difference that causes some trouble.  Mono d0esn’t allow calls to HTTPS unless they come from a “trusted source” – and by default, Mono trusts no one.  Unfortuantely, Google uses HTTPS quite a bit for queries.  The result is that you will likely get WebException or TLSException errors when you try to make queries.

There are supposedly several ways to fix this issue, but I didn’t have luck getting most of them to work, especially under Windows. The approach I took was to override the default Mono security certificate with a custom one that allows connections to HTTPS.  It is fairly simple:

using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class DumbSecurityCertificatePolicy {

    public static bool Validator(
        object sender,
	X509Certificate certificate,
	X509Chain chain,
	SslPolicyErrors policyErrors) {
	    // Just accept and move on...
	    return true;
    }

    public static void Instate() {
        ServicePointManager.ServerCertificateValidationCallback = Validator;
    }
}

Before you make any queries to Google, just be sure you call DumbSecurityCertificatePolicy.Instate() to tell Mono to use this security policy.  To improve on this policy, you could conceivably perform some checks on the parameters passed by Validator before returning true, but I’ll leave that to you, if you feel the added security is needed.

Performing Queries

From here, you can make queries as needed, which are explained in detail in the Spreadsheet API docs.  For example, this is how you might use the SpreadsheetsService to get a list of available spreadsheets:

private void OutputSpreadsheets()
{
    // Create query to get all spreadsheets.
    SpreadsheetQuery getSpreadsheetsQuery = new SpreadsheetQuery();

    // Call to API to get all spreadsheets.
    SpreadsheetFeed spreadsheets = service.Query(getSpreadsheetsQuery);

    // Iterate through returned spreadsheets and find sheet of interest.
    foreach(SpreadsheetEntry spreadsheet in spreadsheets.Entries)
    {
        Debug.Log(spreadsheet.Title.Text);
    }
}

Assuming you’ve created a spreadsheet and know it’s name, you could then access the spreadsheet and use the API to read/write data from/to the spreadsheet.  For example, this function will write a value to an X/Y location on the spreadsheet’s first worksheet:

using UnityEngine;
using System.Collections;
using Google.GData.Client;
using Google.GData.Spreadsheets;

public class GoogleSpreadsheet
{
    private SpreadsheetsService service;
    private SpreadsheetEntry sheet;

    public void SetCell(int x, int y, string stringValue)
    {
            // Assume working with first worksheet.
    	    WorksheetEntry worksheet = (WorksheetEntry)sheet.Worksheets.Entries[0];

    	    // Create a query for the requested cell.
    	    CellQuery cellQuery = new CellQuery(worksheet.CellFeedLink);
    	    cellQuery.MinimumColumn = (uint)x;
    	    cellQuery.MaximumColumn = (uint)x;
    	    cellQuery.MinimumRow = (uint)y;
    	    cellQuery.MaximumRow = (uint)y;

    	    // Get cells meeting the query.
    	    CellFeed cellFeed = service.Query(cellQuery);

    	    // Update the cell.
    	    foreach(CellEntry cellEntry in cellFeed.Entries)
    	    {
    	        Debug.Log("Updating cell " + cellEntry.Title.Text);
    	        cellEntry.InputValue = stringValue;
    	        cellEntry.Update();
    	    }
    }
}

Getting a cell value is very similar to this, except you return the value instead of setting it.  Note that in order to set/get a cell’s value, you must first make sure the worksheets dimensions are large enough.

Finally, this unity package contains a sample Unity editor implementation that can connect to a spreadsheet using either OAuth or ClientLogin.  You can then get or set cell values and see them change in Google Docs.  It isn’t perfect, but it should give you an idea as to how authentication and modification work:

Tagged with →  
Share →

18 Responses to Accessing Google Services via C#

  1. Ranjan says:

    Like your space invaders background. I also like the code highlighting you have here.

    • Clark says:

      Cool, thank you! I can’t claim credit for the background; I found it on a wallpaper website, wallbase.cc. But I think it is pretty awesome myself ;).

  2. Novack says:

    Thanks very much for this article, was very useful indeed!

  3. Maddy says:

    Hi,
    I am trying to integrate oauth – Google with Unity for my game for WebPlayer platform.
    I followed the post :
    http://supersegfault.com/?p=212
    and i can go till i get the access code and i enter that into unity, but when trying to get Access token i am getting an exception:

    SecurityException: Unexpected error while trying to call method_GetSecurityPolicyBlocking : System.Security.SecurityException: Webrequest was denied
    at System.Net.WebConnection.LoggedThrow (System.Exception e) [0x00000] in :0
    at System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request) [0x00000] in :0
    System.Net.WebConnection.LoggedThrow (System.Exception e)
    System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request)
    System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult)
    System.Net.HttpWebRequest.GetRequestStream ()
    Google.GData.Client.OAuthBase.GetOAuth2AccessToken (Google.GData.Client.OAuth2Parameters parameters, System.String requestBody)
    Google.GData.Client.OAuthUtil.GetAccessToken (Google.GData.Client.OAuth2Parameters parameters)

    while getting Access Token through
    “oAuthParams.AccessCode = accessCode;
    OAuthUtil.GetAccessToken(oAuthParams);”

    where “accessCode” = string i get from google.

    what is the issue here.I cant figure that out? Is unity not allowing us to access the web through HttpWebRequest?
    What can be done here? Am i missing something for the web-player ?

    Maddy

    • Clark says:

      I believe this may be caused by the “Security Certificate Policy” – see the section “And Then Mono Made Things Difficult” above. Have you added a custom SecurityCertificatePolicy to your code and then told Mono to use it? If not, try that out. If that isn’t the issue, then some more research will be required.

  4. Maddy says:

    Hi

    Thanks for quick reply. I have added a custom policy as mentioned in the blog. Still no luck.

    Maddy

    • Clark says:

      Hmmm, there isn’t much to go on, unfortunately. Usually when I’ve encountered threads related to this on Unity forums, the response is “use WWW”, which isn’t always possible. Also, since you are using a web player, there could be additional security issues that arise that I haven’t encountered.

      One additional thing you could try is to add a crossdomain.xml file to your project – you might be running into limitations of Unity’s Web Sandbox. Check out this post (and the links in this post) to see if there is anything useful: http://answers.unity3d.com/questions/50013/httpwebrequestgetrequeststream-https-certificate-e.html

  5. Dave says:

    Nice stuff. When I try this, though, I get:

    WebException: The request timed out
    System.Net.HttpWebRequest.GetRequestStream ()
    Google.GData.Client.Utilities.QueryClientLoginToken (Google.GData.Client.GDataCredentials gc, System.String serviceName, System.String applicationName, Boolean fUseKeepAlive, IWebProxy proxyServer, System.Uri clientLoginHandler)
    Rethrow as GDataRequestException: Execution of request failed: https://spreadsheets.google.com/feeds/spreadsheets/private/full
    Google.GData.Client.GDataRequest.Execute ()
    Google.GData.Client.GDataGAuthRequest.Execute (Int32 retryCounter)

    Any ideas?

    • Clark says:

      Do you know at which point in the process this error occurs? Is it during authentication, or when you try to send a query through the API? Also, are you using ClientLogin or OAuth?

      The error makes note of “https://spreadsheets.google.com/feeds/spreadsheets/private/full” – could you be passing this into authentication and Google doesn’t like it?

      One thing you might want to try is wrapping a try/catch clause around the piece of code that is causing the error. Doing so will allow you to examine the GDataRequestException that is thrown, as explained here: http://code.google.com/p/google-gdata/wiki/ErrorHandling

      The error does imply that you are connecting to Google, but they are returning an error. If you can get the error code out of the exception, you might have a better chance of understanding what the problem is.

  6. Hello!

    This is a very well-written & informative post. I’d really like to use it to get at some data in Google SS to make project localization a lot smoother.

    I’ve implemented a test project exactly as above, using ClientLogin auth. I’ve also placed a crossdomain.xml in my Assets folder (confirmed working, been using it for ages to do SQL on my hosted games).

    I seem to be running into an error though, regardless of calling .Instate() in an Awake before doing anything else at all.

    This is the error I’m getting:

    TypeLoadException: Could not load type ‘Google.GData.Spreadsheets.GDataSpreadsheetsNameTable’ from assembly ‘Google.GData.Spreadsheets, Version=2.1.0.0, Culture=neutral, PublicKeyToken=stuffIwasntsureIshouldpaste’.

    Any thoughts? If I can just get at my SS it would really be a dream come true. Thanks for putting this post together!

    • Clark says:

      Hi, glad the post was helpful! Sorry for not getting back to you more quickly; I loaded up the project in Unity and found that I was also getting this error. The only change was that I was now using Unity 4 – not sure what difference that makes. Anyway, I was able to resolve the error by also including the “AccessControl” and “Extensions” DLLs from Google. I’ve updated the article to reflect this.

  7. Krish Vikram says:

    Is it necessary to use Oauth for Writing ?
    I tried this code, without oAuth like this.
    i get cellfeed.entires.count=0.
    I shared the code, could u clarify ?

    https://docs.google.com/document/d/1pFFtbThVOtC9OhiDfzQE1caVvNWHVCQRcchjKS9VG8M

    • Krish Vikram says:

      I used Authentication with ClientLogin and not OAuth2.0 !
      and code doesnt get cellfeed for writing ?

    • Clark says:

      Hi, sorry for the delayed reply! I’m not entirely sure why you’re having that problem, but I’m pretty sure OAuth isn’t necessary to perform writing.

      I think the issue you’re running into is that the worksheet might not have any rows or columns – as a result, when you do your CellQuery, no cells are returned. If you try to output worksheet.Cols and worksheet.Rows, you can determine whether this is the case.

      You might need to add some code such that if you’re trying to set the contents of a cell that is outside of the worksheet’s rows/columns size, you need to resize the worksheet. You can actually easily do this by setting worksheet.Cols and worksheet.Rows and then calling worksheet.Update().

    • Krish Vikram says:

      Thanks.. I sorted it out. I have been trying to write on a empty worksheet, that caused the zero cell returns.
      I used list feed to insert rows & its Working as i wanted.
      Is there anyway, to bypass “using System.Net” library.
      Trial version doesn’t allow building (System.Net.Sockets) ?

    • Krish Vikram says:

      Solved..

  8. Krish Vikram says:

    Issue Solved.

  9. Mohammed says:

    Hello there,

    For some reason the data is not being written onto the spreadsheet.
    I did cellEntry.Update(); and even though it has the data in a value above that ( I insured that by using a mbox) it just wont write.

    Can you help?

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>