Friday, June 14, 2013

Learning TDD

So starting to learn TDD has been on my mind for some time. I've had a few people ask me to use it during my tutorials and I've never really "had time". It would be more appropriate to say that I didn't want to start using it in my daily coding since I don't have to use it during my day job. In other words, it didn't interest me enough to begin using it.

So this week I decided to pick up a book on writing Unit Tests and started a course on TDD. As the instructor of the course said, Writing good tests and doing TDD are 2 completely separate skills. You can follow TDD and still write bad tests. You can also write good tests, but not have a failing test drive your development.

After getting started I realized something yesterday. I started my Intro To Photon videos and blogs. When I became frustrated with the code, I started over again from "scratch" having gained an understanding of how things could be set up. It also allowed me to look at the Server2Server code in the newly released Photon 3.0. When I became frustrated with that code set, I started over yet again and created the Photon Framework. Each of these was better than the previous version and I wanted to look into TDD, but got myself wrapped around the axle for how to "test" code that wasn't mine.

In the end, I decided that my tutorial videos will continue to use the current Photon Framework, but in the background I have started over from scratch yet another time. This time, however, I started with TDD. I started with nothing more than the notion of a server that could add and remove clients from a list. Since yesterday, I have created a suite of 32 tests that cover 12 functions which are helping me keep to my "design specifications" that I have built over the last 3 iterations of the same set of code.

The more I play with TDD, the more I like how it sets me up to accomplish what I want to do and still gives me the ability to refactor it and ensure that the same basic rules still apply.

Wednesday, June 5, 2013

Value types vs. Reference Types

I won't claim to be an expert by any stretch of the imagination. However, I have learned a few tricks in dealing with the conversion of C++ libraries to C#.

Let me begin with a bit of background. I enjoy working with Unity3D. It has one small "problem", to work with C++ you have to buy the professional version, a small setback of 1500$. To counteract this problem, I've been working to convert a few C++ open source libraries to C#. Namely Recast Navigation and Bullet Physics.

"Why bother," you might ask? Well for one, I can't afford the 1500$ price tag for Unity3D. For another, taking apart the library helps me understand how it works. Furthermore, I can code Editor and Inspector enhancements for Unity3D to take advantage of the library.

Back to the topic at hand. A value type, when working with C++ and C#, is an object that contains its data directly. A reference type, on the other hand, is a reference to an object that contains its data. Let me explain it another way. If I hand you an apple, it would be a value type, I am passing the object directly to you. If I hand you a gift voucher for an apple, I still gave you an apple, but unlike the physical apple, when you go to get the apple for the voucher, it may not be the same one pictured on the voucher.

So what does that have to do with C++ and converting to C#?

In C++ everything is a value type, both class and struct. If you want to pass a reference to it, so you can modify it and give it back in the modified state, you have to do one of two things, either pass by reference by adding an &, or pass by pointer meaning you add a * at creation. Without this, when it gets to the function, if you make any changes, those changes are ignored when it gets back to the area that called it.

In C# classes are a reference type, this means that if I do A = B; A now points to the same apple B points to, if you want to make a copy of B to use with A, you have to create a new object, pass in the values of B, and give that to A. A = new Foo(B);

There is a handy little trick when it comes to C#. If you wish to always pass by value, that is A = B; gives a copy of A, you simply declare the class as a struct instead: public struct Foo{};

One minor word of caution, when you use structs, you cannot inherit from other structs or classes, only from interfaces. This could lead to code duplication if you are attempting to make a derived struct.

Anyway, this was today's lesson for me as I work through the conversion of Bullet Physics.

Wednesday, May 29, 2013

Building Libraries

So in my spare time, I like to play around with small components of various games and try to write them into small libraries. The nice thing about doing it this way is that it has no ties to a specific game. So the last few weeks I've been playing with a few different libraries.

First up, Behavior Trees. I was on AIGameDev.com and they have a few posts covering Behavior Trees rather than using Finite State Machines. So I looked at their code and built up a small library with some test cases to play around with their concept. I ran into a snag in that once it successfully made it through the behavior tree, you had to rebuild the tree, or hack the code to reset it. So after some more digging around I found snippits of other behavior trees and was able to make a small library with various nodes and put together a small demo in Unity3d showing a block moving to other blocks to "eat" them when it got hungry.

After that I started up the previous blog post and decided to begin looking at small scripts to handle cameras for most Strategy games (top down, scrolls when the mouse is near the edge, zoom with scroll wheel) and I've decided to make a permanent home for my musings. So far it only does some simple camera movement.

Today I'm looking into how to use Unity3D to make a radar system. I want to build it to behave both like an MMO with the player in the center, and like an RTS, with a camera frustum outline and the entire map displayed so you can click and zoom to that location.

As with everything I work on, I hope to build these into tutorials so that others can benefit from my musings as well.

Monday, May 27, 2013

Starting the blog again - 4X and MMO Games

I've decided I'm going to use this blog to put my thoughts on paper while I play with various mechanics and play around with game ideas. Today I wanted to begin with just making a simple camera controller that scrolls at the edges of the screen. Which leads to me why I chose this simple project.

I've always been a big fan of what are called 4X games. Specifically I enjoy the Turn Based Strategy games such as Civ 4, Masters of Orion, Space Empires 4. For those that don't know, 4X stands for eXplore, eXpand, eXploit, eXterminate. Most feature pretty indepth tech trees, have multiple paths to victory and can suck many hours of your life away.

A few games have tried to make this take the form of an MMO. EVE Online could be classified as a 4X game, Mankind.net is a smaller, nitch game that has changed a few hands, and Beyond Protocol was an indie attempt and probably could have done much better than it did.

So I find myself thinking about what I would do to make a 4X MMO game. It is something that has crossed my mind before, but not in great detail outside of saying that I'd want to make it a cross between EVE Online and Civilization 4.

I like several concepts from various games I've seen over time. A couple of them are Component Configuration and Design.

Space Empires, EVE Online, and StarDrive all share at least one concept: ship configuration. Whether you choose a ship because it has strong shields, or you design the ship from the ground up with a specific hull and add shields or armor, all of these games let you configure the ship the way you want.

Star Wars Galaxies, before NGE, and Beyond Protocol had an awesome setup with components that you designed. Depending on the materials you used, a weapon could be created with drastically varied stats. This leads to a more nitch market of gamers who like to min/max things and like to play with designs to get the most out of their materials.

Lastly, one thing I've covered before is material acquisition. EVE Online, Star Wars Galaxies, Beyond Protocol, and Saga of Ryzom have all covered interesting aspects. EVE Online has you able to get materials from mining, but the materials you can mine vary depending on where you are, from safe space to null security space. Star Wars Galaxies had materials that changed on a week by week basis leading to vast databases created to know what material was available on which planet during a given week. Saga of Ryzom had harvesters who could search for harvest nodes using skills, and Beyond Protocol had a bid system if more than one person was on a planet with a mine, you would bid on the minerals with the top 4 people getting 25% of the minerals each, if only 2 people bid on the mine, 25% went to the second person and the remaining to the top bidder, if there were 3, 25% went to the bottom 2 and 50% went to the top bidder.

These 3 things can be pretty powerful and add a subtype to the genre that allows people more specific play style. I want to go over more of them in detail while I play with my development.

Tuesday, June 28, 2011

Moving over to drupal

So I have been working with blogger for a while now but it has several limitations. It doesn't do well when working with code. It has some tags you can use to make your life a little easier or you can code the css, but it is still a pain. On top of that it has no way for me to put files up for download except if i upload them first. Lastly it is only good for blogging. I've had a web server up and running but only used it sparingly. Today I changed that. I installed drupal and have begun using it. I spent the night porting over all the tutorials and setting up the site and its ready to go now. It has all the same tutorials, but I will only be posting to it in the future. So if you please redirect your links and bookmarks to http://cjrgaming.com you will be taken to the new site. I continue to look forward to working more with Unity3d and doing client-server technology.

Friday, June 24, 2011

Foray into Photon - Part 17 - Extracting the SessionFactory

So it seems that somehow this part was deleted yesterday, so I'm reposting it again today. We will be moving the ISessionFactory out of AegisBornPeer and instead be putting it into its own Singleton and using the Factory Method for creating new sessions. Go ahead and open up AegisBornPeer and delete CreateSessionFactory, the SessionFactory property, and the initialization statement in the constructor. Next we will be creating a new class called NHibernateHelper. As I said before it will be using the Singleton and Factory Method patterns. Here is the entire class:


using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;


namespace AegisBorn
{
    public class NHibernateHelper
    {
        private static ISessionFactory _sessionFactory;


        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                    InitializeSessionFactory();
                
                return _sessionFactory;
            }
        }


        private static void InitializeSessionFactory()
        {
            _sessionFactory = Fluently.Configure()
              .Database(
                MySQLConfiguration.Standard
                .ConnectionString(cs => cs.Server("localhost")
                .Database("cjrgam5_ab")
                .Username("cjrgam5_ab")
                .Password("user_ab1!")))
              .Mappings(m =>
                m.FluentMappings.AddFromAssemblyOf())
              .BuildSessionFactory();
        }


        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}

As you can see, the InitializeSessionFactory is almost exactly the same as CreateSessionFactory was except that we store the value locally in the singleton. Next you can see the factory method OpenSession that returns a new session to us. In AegisBornPeer you can replace the call to CreateSessionFactory with NHibernateHelper.OpenSession(); This also gives us the ability to use it in other listeners and not just the peer.

This lesson is rather small, next time we will be implementing the rest of the client side of the login by switching scenes and doing the setup necessary for the character select. Stay tuned.

Thursday, June 23, 2011

Foray into Photon - Part 16 - Gearing up for NHibernate

So today we are going to dive into NHibernate. NHibernate is the .NET implementation of Hibernate which we used in our SmartFoxServer implementation. Java had annotations which made it easier to work with for entities. We don't have the same luxury in .NET, but we do have an add-on called FluentNHibernate which extends NHibernate to allow us to easily define mappings without making .hbn.xml files for ever class. So first you will want to go to NHibernate Forge and pull down NH3.1.0. Then you will want to go to Fluent NHibernate and download version 1.2 for NHibernate 3.1. You will also want to go to MySQL and download the .NET driver for MySQL.

Once you have these downloaded, i created a folder called AegisBornLibs which contains any libraries we are going to use. Extract the 2 zip files to this folder. Then you will want to run the installer for the MySQL driver. Next we want to open the AegisBornPhoton project and add the .dlls to the AegisBorn project. The files we want are FluentNHibernate.dll, NHibernate.ByteCode.Castle.dll, and Required_Bins\NHibernate.dll

Once these references are added to our project we are going to create our first entity classes - SfGuardUser, AegisBornCharacter, and AegisBornUserProfile. Again these are just like the SmartFoxServer model classes. We will be creating these classes in a folder called Models\Base. Before I create them, I go ahead and set up a server connection in the server explorer. Once my connection is established I create the Models\Base and Models\Map directories in my solution. From there I create the above 3 classes and my first map file:


    public class SfGuardUser

    {
        public virtual int Id { get; set; }
        public virtual string Username { get; set; }
        public virtual string Password { get; set; }
        public virtual string Salt { get; set; }
    }



    public class AegisBornUserProfile
    {
        public virtual int Id { get; set; }
        public virtual SfGuardUser UserId { get; set; }
        public virtual int CharacterSlots { get; set; }
    }



    public class AegisBornCharacter
    {
        public virtual int Id { get; set; }
        public virtual SfGuardUser UserId { get; set; }
        public virtual string Name { get; set; }
        public virtual string Sex { get; set; }
        public virtual string Class { get; set; }
        public virtual int Level { get; set; }
        public virtual int PositionX { get; set; }
        public virtual int PositionY { get; set; }
    }


    public class SfGuardUserMap : ClassMap
    {
        public SfGuardUserMap()
        {
            Id(x => x.Id).Column("id");
            Map(x => x.Username).Column("username");
            Map(x => x.Password).Column("password");
            Map(x => x.Salt).Column("salt");
            Table("sf_guard_user");
        }
    }

I stopped after creating SfGuardUserMap because at this point I wanted to test my code and ensure that it all works. To do this I built my function in AegisBornPeer that handles the login call. It looks like this:


        public ISessionFactory SessionFactory { get; set; }


        [Operation(OperationCode = (byte)OperationCode.Login)]
        public OperationResponse OperationLogin(Peer peer, OperationRequest request)
        {
            var operation = new LoginSecurely(request);
            if(!operation.IsValid)
            {
                return new OperationResponse(request, (int)ErrorCode.InvalidOperationParameter, operation.GetErrorMessage());
            }


            // Attempt to get user from db and check password.
            try
            {
                using (var session = SessionFactory.OpenSession())
                {
                    using (var transaction = session.BeginTransaction())
                    {
                        var user = session.CreateCriteria(typeof(SfGuardUser), "sf").Add(Restrictions.Eq("sf.Username", operation.UserName)).UniqueResult();


                        var sha1 = SHA1CryptoServiceProvider.Create();


                        var hash = BitConverter.ToString(sha1.ComputeHash(Encoding.UTF8.GetBytes(user.Salt + operation.Password))).Replace("-", "");


                        transaction.Commit();


                        if (String.Equals(hash.Trim(), user.Password.Trim(), StringComparison.OrdinalIgnoreCase))
                        {
                            return operation.GetOperationResponse(0, "OK");
                        }
                    }
                }
            }

            catch (Exception)
            {
                // Do nothing because we are about to throw them out anyway.
            }


            peer.PublishOperationResponse(new OperationResponse(request, (int)ErrorCode.InvalidUserPass, "The Username or Password is incorrect"));
            peer.DisconnectByOtherPeer(this, request);
            return null;
        }



        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
              .Database(
                MySQLConfiguration.Standard
                .ConnectionString(cs => cs.Server("localhost")
                .Database("cjrgam5_ab")
                .Username("cjrgam5_ab")
                .Password("user_ab1!")))
              .Mappings(m =>
                m.FluentMappings.AddFromAssemblyOf())
              .BuildSessionFactory();
        }

The CreateSessionFactory function is only in this file while I test this code. Next post we will be extracting it into a helper class that will Lazy create the connection the first time we try to access the database. For more information on the configuration jump on over to http://wiki.fluentnhibernate.org/Database_configuration where you can see what each piece does. As for the Operation handler, we first make sure the operation is valid, meaning it has UserName and Password fields. After that we are following NHibernate's process for accessing the database, we create a session, then a transaction, then our query. Our query looks up the SfGuardUser by username and then rebuilds the password using the salt and our provided password and running it through an SHA1 digest. If the password isn't correct or there is no user, we throw errors. 

The last changes we are going to make are to create a new error code called InvalidUserPass:

        ///
        /// The username or password isn't correct, don't let them log in.
        ///
        InvalidUserPass,

Now we have the catch and final returns to pass an InvalidUserPass error so the user doesn't know if they exist or not which will help with hacking attempts. We also kick them off the server, a handy feature.

So thats all this time. Next time we will make a helper class for NHibernate's session factory. Until then, enjoy!

Wednesday, June 22, 2011

Foray into Photon - Part 15 - LoginOperation client side

This time around we are going to create our first real operation. I am going to start off in the AegisBornCommon project as we need to add some data there first. First we need to pull up the OperationCode.cs file. We are going to add a new operation code. In projects past, I tend to lump messages into groups. It makes it easier to know what type of message you are working with if you have your codes in groups. For this instance we are going to take numbers 100-110 for any operations before we are fully in the game. Exit Games uses some numbers for their own special operations and parameters. They have stated that operations 100 and up and Parameters 61 and up are open so for now just add this after Nil,

        /// <summary>
        /// Login to the server
        /// </summary>
        Login = 100,

Next we need to add our parameter codes after DebugMessage in ParameterCode.cs:

        /// <summary>
        /// The player's username
        /// </summary>
        UserName = 70,

        /// <summary>
        /// The player's password
        /// </summary>
        Password = 71,

Now that we have our operation code and parameter code, we can recompile the Common project and make sure it is in the same directory as our AegisBorn3dPhoton project so that Unity3d can use it. Now if you read through the SmartFoxServer blog posts, you'll know I hate building stuff manually, meaning if I'm going to build a message to send to a server, I build it in a class/function so that I don't accidentally have 15 versions floating around in code. Exit Games did this nicely with a static class called Operations. However, I feel lumping them all into the same class defeats the purpose of a class. So I'm going to build a file called LoginOperations and I'm going to put it in a directory called _Operations under _Scripts:

using System.Collections;
using AegisBornCommon;

public static class LoginOperations
{
    public static void Login(Game game, string username, string password)
    {
        var vartable = new Hashtable
                           {
                               {(byte) ParameterCode.UserName, username},
                               {(byte) ParameterCode.Password, password}
                           };

        game.SendOp(OperationCode.Login, vartable, true, 0, true);
    }
}

And that will now send the login operation to the server. Next we are going to grab our old GUI code from the SmartFoxServer code and put it into the Login.cs OnGUI with some minor modifications:

    private Game _engine;


    void Start()
    {
        _engine = MMOEngine.Engine;
        _engine.afterKeysExchanged += AfterKeysExchanged;
    }


    private string _username = "";
    private string _password = "";


    void OnGUI()
    {


        GUI.Label(new Rect(10, 116, 100, 100), "Userame: ");
        _username = GUI.TextField(new Rect(100, 116, 200, 20), _username, 25);
        GUI.Label(new Rect(10, 141, 100, 100), "Password: ");
        _password = GUI.PasswordField(new Rect(100, 141, 200, 20), _password, '*', 25);


        GUI.Label(new Rect(10, 225, 400, 100), _engine.State.ToString());


        if (GUI.Button(new Rect(100, 165, 100, 25), "Login") || (Event.current.type == EventType.keyDown && Event.current.character == '\n'))
        {
            var peer = new PhotonPeer(_engine, false);


            _engine.Initialize(peer, "localhost:5055", "AegisBorn");
        }
        if (GUI.Button(new Rect(100, 195, 100, 25), "Logout"))
        {
            _engine.SetDisconnected(0);
        }
    }


    public void AfterKeysExchanged()
    {
        LoginOperations.Login(_engine, _username, _password);
    }

With encryption in place, we are now sending the server our username and password encrypted. The server will only send a response stating that it got the message. 


If you were to run the program at this point, you will see that we have our login screen, when you click Login you will see the state transfer from Disconnected to WaitingForConnect to Connected. The log should have a message stating connected and then Keys Exchanged and then an unknown response to our Login operation.


Next time we will look into modifying the server with NHibernate to begin looking up our data and checking our login information.


As an aside, I've joined Empire Avenue. {EAV_BLOG_VER:caf99dc9611a09b5}

Foray into Photon - Part 14 - Migrating to Operation and Event Handlers

So before we get into creating and sending the Login operation we are going to modify our GameStates to have a dictionary of OperationHandlers and EventHandlers. But first we need a set of objects to base our handlers off of. I began by opening Unity3d and creating a new folder called _Handlers. Inside that folder I created a folder called Operations and another one called Events. In Operations I created a new class called IOperationHandler and it looks like this:


using System.Collections;
using AegisBornCommon;


public abstract class IOperationHandler
{
    public delegate void BeforeMessageRecieved();
    public BeforeMessageRecieved beforeMessageRecieved;


    public delegate void AfterMessageRecieved();
    public AfterMessageRecieved afterMessageRecieved;


    public void HandleMessage(Game gameLogic, OperationCode operationCode, int returnCode, Hashtable returnValues)
    {
        if (beforeMessageRecieved != null)
        {
            beforeMessageRecieved();
        }
        OnHandleMessage(gameLogic, operationCode, returnCode, returnValues);
        if (afterMessageRecieved != null)
        {
            afterMessageRecieved();
        }
    }


    public abstract void OnHandleMessage(Game gameLogic, OperationCode operationCode, int returnCode, Hashtable returnValues);
}


If you notice, this class is exactly like our IMessageHandler from the SmartFoxProject. The only difference is the HandleMessage and OnHandleMessage where we pass along the game, the op code, the return code and the table of values.


Then we need to wire in the handlers to the IGameState:


    Dictionary OperationHandlers { get; }

This means we need to change our Disconnected and WaitingForConnect to include the following:

    public Dictionary OperationHandlers
    {
        get { throw new NotImplementedException(); }
    }

So we will get an error if Disconnected or WaitingForConnect tries to load our handlers. Next we need Connected to be modified. Here we will create a real dictionary and set it up:

    private readonly Dictionary _handlers;
    public Dictionary OperationHandlers
    {
        get { return _handlers; }
    }

    public Connected()
    {
        _handlers = new Dictionary();
        // Add handlers here
        var keyHandler = new ExchangeKeysHandler();
        _handlers.Add(OperationCode.ExchangeKeysForEncryption, keyHandler);
    }

So here we have created our handler dictionary and added a new class that we haven't created yet called ExchangeKeysHandler. The last thing we are going to do before we create this new class is make the modifications to look through our list of handlers and attempt to find one to handle this method:

    public void OnOperationReturn(Game gameLogic, OperationCode operationCode, int returnCode, Hashtable returnValues)
    {
        IOperationHandler handler;

        if (_handlers.TryGetValue(operationCode, out handler))
        {
            handler.HandleMessage(gameLogic, operationCode, returnCode, returnValues);
        }
        else
        {
            gameLogic.OnUnexpectedPhotonReturn(returnCode, operationCode, returnValues);
        }
    }

This code does like it does in the SmartFox code, it attempts to find a handler and if one isn't found, we get an unexpected return which prints a message in our debug stating that we couldn't handle it.

So now we need to build our ExchangeKeysHandler class which we will create in the Operations folder under _Handlers:

using System.Collections;
using AegisBornCommon;

public class ExchangeKeysHandler : IOperationHandler
{
    public override void OnHandleMessage(Game gameLogic, OperationCode operationCode, int returnCode, Hashtable returnValues)
    {
        gameLogic.Peer.DeriveSharedKey((byte[])returnValues[(byte)ParameterCode.ServerKey]);
        gameLogic.NotifyKeysExchanged();
    }
}

This contains the code that was originally in the switch statement with the exception of the handled boolean to tell us we handled it.

Now we can handle messages in any of our IGameStates by creating a new class of type IOperationHandler and adding it to our list of handlers in our game state. Again, if you are interested, this is already checked into the github repository, which has now been split between the smartfox and photon versions. Links are in the right hand column.

Foray into Photon - Part 13 - Exchanging keys for encryption

So before we create our login, we need to take the time to handle encryption. There are several file modifications we must perform to pass along when we want to encrypt an operation. We begin with IGameState. The last function is SendOperation, this now changes to:


    void SendOperation(Game gameLogic, OperationCode operationCode, Hashtable parameter, bool sendReliable, byte channelId, bool encrypt);

Because of this change we have to modify Disconnected, WaitingForConnect, and Connected. Modify each of those classes so they also have bool encrypt at the end. Lastly we need to modify the code inside Connected.SendOperation to be the following:

    public void SendOperation(Game gameLogic, AegisBornCommon.OperationCode operationCode, System.Collections.Hashtable parameter, bool sendReliable, byte channelId, bool encrypt)
    {
        gameLogic.Peer.OpCustom((byte)operationCode, parameter, sendReliable, channelId, encrypt);
    }

Disconnected and WaitingForConnect only need the first change because they have no code in send operation.

With this change in place we need to modify Game.SendOp to pass along an encrypt:

    public void SendOp(OperationCode operationCode, Hashtable parameter, bool sendReliable, byte channelId, bool encrypt)
    {
        _stateStrategy.SendOperation(this, operationCode, parameter, sendReliable, channelId, encrypt);
    }

Now that we are set up to pass along encrypted messages, we need to exchange keys with the server. The best place for this would be in Game.SetConnected because it ensures we are connected to the server and we can handle the response in Connected's handling code. Here is the new function:

    public void SetConnected()
    {
        _stateStrategy = Connected.Instance;
        _listener.OnConnect(this);
        _peer.OpExchangeKeysForEncryption();
    }

    public delegate void AfterKeysExchanged();
    public AfterKeysExchanged afterKeysExchanged;

    public void NotifyKeysExchanged()
    {
        if (afterKeysExchanged != null)
        {
            afterKeysExchanged();
        }
        _listener.LogInfo(this, "Keys successfully exchanged");
    }

In Connected.cs we need to handle the response as it comes back from the server in OnOperationResponse:

        var handled = false;

        switch(operationCode)
        {
            case OperationCode.ExchangeKeysForEncryption:
                gameLogic.Peer.DeriveSharedKey((byte[])returnValues[(byte)ParameterCode.ServerKey]);
                gameLogic.NotifyKeysExchanged();
                handled = true;
                break;
        }

        if (!handled)
        {
            gameLogic.OnUnexpectedPhotonReturn(returnCode, operationCode, returnValues);
        }

At this point you will notice we need to add a few things to AegisBornCommon. Lets do that now. In OperationCode.cs we need to add the ExchangeKeysForEncryption code:

        ///
        /// Code for exchanging keys using PhotonPeer.OpExchangeKeysForEncryption
        ///
        ExchangeKeysForEncryption = 95,

Then we need to add a couple values to the ParameterCode.cs file:

        ///
        /// Client key parameter used to establish secure communication.
        ///
        ClientKey = 16,

        ///
        /// Server key parameter used to establish secure communication.
        ///
        ServerKey = 17,

Once you recompile and ensure the new dll is in the Plugins directory in your Unity3d project the compile errors will disappear and you will be ready to implement the server portion of our key exchange.

First we need to create an operation to handle the exchange. I created a folder in AegisBorn called Operations and inside I created the EstablishSecureCommunicationOperation:

using AegisBornCommon;
using Photon.SocketServer;
using Photon.SocketServer.Rpc;

public class EstablishSecureCommunicationOperation : Operation
{
    public EstablishSecureCommunicationOperation(OperationRequest operationRequest)
        : base(operationRequest)
    {
    }

    ///
    /// Gets or sets the clients public key.
    ///
    [RequestParameter(Code = (short)ParameterCode.ClientKey, IsOptional = false)]
    public byte[] ClientKey { get; set; }

    ///
    /// Gets or sets the servers public key.
    ///
    [ResponseParameter(Code = (short)ParameterCode.ServerKey, IsOptional = false)]
    public byte[] ServerKey { get; set; }
}

This class' job is to take the information from the client, create a key, and send back a key to the client that the client will use when sending encrypted messages. Next we need to add our code to the dispatcher AegisBornPeer:

        [Operation(OperationCode = (byte)OperationCode.ExchangeKeysForEncryption)]
        public OperationResponse OperationKeyExchange(Peer peer, OperationRequest request)
        {
            var operation = new EstablishSecureCommunicationOperation(request);
            if (operation.IsValid == false)
            {
                return new OperationResponse(request, (int)ErrorCode.InvalidOperationParameter, operation.GetErrorMessage());
            }

            // initialize the peer to support encrytion
            operation.ServerKey = peer.PhotonPeer.InitializeEncryption(operation.ClientKey);

            // publish the servers public key to the client
            return operation.GetOperationResponse(0, "OK");
        }

Thanks to the work we did earlier, this operation is automatically handled. It initializes the encryption and returns an OK response to the client. At this time I also went ahead and added the following code to the ErrorCode.cs file which comes directly from the Exit Games Demo:

        ///
        /// The parameter out of range.
        ///
        ParameterOutOfRange = 51,

        ///
        /// The operation not supported.
        ///
        OperationNotSupported,

        ///
        /// The invalid operation parameter.
        ///
        InvalidOperationParameter,

        ///
        /// The invalid operation.
        ///
        InvalidOperation, 

With all this in place, when you click connect, you will get a message says Keys successfully exchanged. At this point we are ready to send an encrypted message to the server. You will notice that I used a switch statement in Connected.OnOperationResponse. We will be removing this and adding Operation Handlers in the near future. If you pull down the latest GitHub code, I already have them in place as well as what we will cover in the next blog, which is sending a login operation.

Tuesday, June 21, 2011

Foray into Photon - Part 12 - Seperating the Engine from the GUI

So today I hope to walk through the code necessary to log in. We are going to start with the client code as that is probably the easiest to work with. The first thing I want to do is separate the the IGameListener from the GUI login code. This is because we don't want to keep creating listeners that contain the same code. So lets go ahead and create a new script called MMOEngine and keep the Login script. Here is the MMOEngine code - it is basically all the code from login except the OnGUI code, the Start function is now Awake and the Game variable is now static:


using System;
using ExitGames.Client.Photon;
using UnityEngine;


public class MMOEngine : MonoBehaviour, IGameListener
{

    private static Game _engine;


    public static Game Engine
    {
        get { return _engine; }   
    }


    // Use this for initialization
    void Awake()
    {
        Application.runInBackground = true;
        _engine = new Game(this);
    }


    // Update is called once per frame
    void Update()
    {
        try
        {
            _engine.Update();
        }
        catch (Exception e)
        {
            Debug.Log(e);
        }
    }


    ///


    /// The on application quit.
    ///

    public void OnApplicationQuit()
    {
        try
        {
            _engine.Disconnect();
        }
        catch (Exception e)
        {
            Debug.Log(e);
        }
    }


    #region Inherited Interfaces


    #region IGameListener
    public bool IsDebugLogEnabled
    {
        get { return true; }
    }


    public void LogDebug(Game game, string message)
    {
        Debug.Log(message);
    }


    public void LogError(Game game, string message)
    {
        Debug.Log(message);
    }


    public void LogError(Game game, Exception exception)
    {
        Debug.Log(exception.ToString());
    }


    public void LogInfo(Game game, string message)
    {
        Debug.Log(message);
    }


    public void OnConnect(Game game)
    {
        Debug.Log("connected");
    }


    public void OnDisconnect(Game game, StatusCode returnCode)
    {
        Debug.Log("disconnected");
    }


    #endregion


    #endregion
}

The new code for Login.cs is this:


using System.Collections;
using UnityEngine;
using ExitGames.Client.Photon;
using AegisBornCommon;


public class Login : MonoBehaviour
{

    private Game _engine;


    public void Start()
    {
        _engine = MMOEngine.Engine;
    }


    public void OnGUI()
    {
        if (GUI.Button(new Rect(100, 60, 100, 30), "Connect"))
        {
            var peer = new PhotonPeer(_engine, false);


            _engine.Initialize(peer, "localhost:5055", "AegisBorn");
        }
        if (GUI.Button(new Rect(200, 60, 100, 30), "Send Operation"))
        {
            _engine.SendOp((OperationCode)100, new Hashtable(), false, 0);
        }
        GUI.Label(new Rect(100, 100, 300, 300), _engine.State.ToString());
    }
}

Now that we have them separated, we want to create a new game object called MMOEngine and attach our script to it. This now makes it like our SmartFoxServer code where we had the connection handler get a connection to the SmartFoxConnection class.

Nothing too dramatic this time. Next time we will be pulling over our GUI code from our other project and sending an operation that asks to log in. Enjoy!