Tuesday, December 28, 2010

Kongregate Unity3D competition

So it came to my attention last week that Kongregate and Unity3D were doing a competition. 1st place prize is 10k$. Not bad considering once your game is up in Kongregate you make money from their ad revenue. So my partner and I decided we would try an enter. Here is the Contest Page.

So today I'm looking over the current competition and played Sara's Run, by SophieHoulden. Then I saw that it was a preview because she is working on another game. I went and looked and my heart sank. She is working on a game called Lottie's Dungeon and reading the feature list and what she wants to do with it is almost exactly like what I wanted to do for the contest. The only problem is that she is 2 months farther along than I am.

In any case, I still plan to move forward with my idea for the contest. It has some interesting ideas that work well and will teach me some new techniques. I just need to sit down and work on it. Jealousy can't hold me back. Anyway, I guess tomorrow I need to sit myself down and get some work done so that I can get where I want to with my game.

Tuesday, December 21, 2010

Unity 3D Inventory Management Project

Many people, myself included, are struggling with building a nice inventory system for their RPG. I'd like to start this next set of tutorials exploring how we can set up an inventory system. Laying out how we want to store items, what we can do with them, and how to interact with them. I'm going to try and make it as generic as possible so that it doesn't require alot of work to put into any game and will have configurable options.

So I'm going to begin by defining the various things that make up an inventory system. First it needs to be able to be applied to anything. This means that any object in the game can be a container. Normally you want to limit it to backpacks, chests, dead critters, or even the ground, and lastly the player. Next we need to decide if we can stack things together into groups. Do we want to have a set inventory size, like Dungeon Siege where objects take up space and space is limited. Do we want to allow weight where an object can only carry so much, like Everquest and Asheron's Call. Do we want to limit the number of types of items per container, like WoW's and Asheron's Call's backpack setup. With that in mind we are going to create a config that will accommodate as much as possible and give the developer a way to choose which version he wants.

Given this list of requirements we will need a couple of classes:

  • Item
  • Inventory
  • GameItem
  • GameItemInventory
  • ItemManagementConfig
Now you may be thinking that we are duplicating our classes by having an Item and a GameItem and same with Inventory and GameItemInventory. I am separating them because we want one for a data structure that can be easily passed to us from a server and one for scripts which will go on the game objects. 

You could easily argue that you could combine them, however, because we are dealing with data from the server I prefer to use separate objects and let the scripts have a reference to the class that contains their data.

So lets begin by laying out our ItemManagementConfig class. This class will define what options we will use. The enumeration does a good job for picking which of these options we will allow. I have chosen Weight, Slots, and Stackable items. We will be writing our code generically enough in the inventory and item systems that will make this work well together.


public class ItemManagementConfig {

public enum BagType { WEIGHT, SIZE, SLOTS, WEIGHT_AND_SLOTS };

public static BagType bagType = BatType.WEIGHT_AND_SLOTS;
public static bool usesStacks = true;

}


Pretty simple, but it's only our config class. Next time we will be defining our Item class and using these configurations to set up some basic stats that our generic items will have. And once we get further along we will expand our list of item types to include armor, weapons, consumables, and anything else we may need.

Monday, December 20, 2010

Unity3D Successful Login

This is the last of these tutorials. With this we will have a fully working setup where you can create users. You can allow them to log into your web application. And you can use the same login to allow them into your game. This also allows you to develop the web site using the same database so data will match between your game and your web site which is handy as it is one less thing to keep up to date in 2 places fulfilling our DRY principle.

Lets start by opening LobbyGUI and adding our new handler under the publickey handler:


handlers.Add("loginsuccess", OnLoginSuccess);


and lastly we add our OnLoginSuccess function:


    void OnLoginSuccess(ISFSObject data)
    {
        UnregisterSFSSceneCallbacks();
        Application.LoadLevel("CharacterSelect");
    }

We need to make sure we unregister the SFS callbacks or you will end up with some funky messaging errors. Lastly we load the new level. Make sure you create this scene and add it to the build properties, otherwise you'll get an error trying to load the new scene.

There you have it. We took Symfony, Unity3D, and SmartFoxServer and wrote the beginning to your very own online game. From here you can expand it however you like.

I am considering putting the files together to a downloadable for a nominal fee, say $10. If enough interest is generated, I'll work on packaging up the extension, entity project, and Unity3D code for people. Leave a comment if you would like to see this happen.

SmartFoxServer Decrypt Login Message

Here we are, nearly to where we will officially let our user into the system as a registered user and give them access to the game. We now need to decrypt the incoming data, verify the user against the database, and kick them off or let them into the full game. To start we will need to create a handler so right click [domain.domainname.gamename].handlers and add a new Java class. This will be called DBLoginHandler.

We will start it off with:


package [domain.domainname.gamename].handlers;


import [domain.domainname.gamename].[GameName]Extension;
import [domain.domainname.gamename].entities.SfGuardUser;
import [domain.domainname.gamename].core.AegisBornAccount;
import [domain.domainname.gamename].core.World;
import [domain.domainname.gamename].util.RoomHelper;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.extensions.BaseClientRequestHandler;
import java.math.BigInteger;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.persistence.EntityManager;
import javax.persistence.Query;


/**
 *
 * @author Christian
 */
public class DBLoginHandler extends BaseClientRequestHandler {


    @Override
    public void handleClientRequest(User u, ISFSObject data) {
        try {

        } catch (Exception e) {
            trace(e);
        }
    }
}

Just your typical handler. Nothing particularly interesting. Now lets add the rest of the code chunk by chunk and I'll explain along the way. All of this code will go in handleClientRequest in the try block in the correct order.


            // Get the world and add the player.
            World world = RoomHelper.getWorld(this);
            AegisBornAccount player = world.getPlayer(u);
            Cipher cipher = player.GetCipher();
            ISFSObject keyData = data.getSFSObject("login");
            String user = new String(cipher.doFinal(keyData.getByteArray("user")));
            String password = new String(cipher.doFinal(keyData.getByteArray("password")));

This first chunk goes in and gets our world. It uses the world to load our player. Then we request our cipher from the player. This will return our decrypt cypher which uses are server's private key to decrypt the data the client requested. We get our login object from our incoming data. We then get the key user and password and pass it to our cipher to the doFinal function which decrypts the data and returns our string.

            EntityManager entityManager = (([GameName]Extension)this.getParentExtension()).getEntityManagerFactory().createEntityManager();
            entityManager.getTransaction().begin();
            Query q = entityManager.createNamedQuery("SfGuardUser.findByUsername", SfGuardUser.class);
            q.setParameter("username", user);
            SfGuardUser guard = (SfGuardUser)q.getSingleResult();

This next chunk loads up the entity manager from the extension and starts a transaction. It then creates a named query which is created for us by OpenJPA. We then return a single result from it, which should be our 1 user. We will put in the function getEntityManagerFactory when we finish with this class.

            // Execute query
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.reset();
            digest.update(guard.getSalt().concat(password).getBytes());
            byte[] sha1Password = digest.digest();
            BigInteger bi = new BigInteger(1, sha1Password);
            String result = bi.toString(16);
            if (result.length() % 2 != 0) {
                result = "0" + result;
            }
            trace("Result: " + result);
            if (guard.getPassword().equalsIgnoreCase(result)) {
                player.setGuardUser(guard);
                 send("loginsuccess", new SFSObject(), u);
            } else {
                this.getApi().kickUser(u, null, "Username or Password is incorrect.", 4);
            }
This last chunk creates a Digest and loads the SHA-1 algorithm. This is the default algorithm used by Symfony's SfDoctrineGuardPlugin. We then update the digest with the salt column of the database concatinated with our user's typed password. We then get a byte array after the digest converts it. In order to check the password, we need to load it into a new BigInteger and use it's toString() function to convert it into a hex string. The if block is a catch because every number in hex is 2 string values from 0-9 and a-f. If our value isn't even in length, it means the leading 0 was dropped and we must add it back in. The last if checks to see if the password in the database matches the password we just created combining the salt and typed in password. If it does, we return a loginsuccess message. We don't need to create a real object for it because we are just telling our client it is now truly logged in. If the password doesn't match, we kick the user off the server. We do this because we allowed the user to log in without a password because SmartFox during login encrypts the password in its own fashion and there isn't a way to get the clear text version which is what we need to recreate our digest.

Now that we have our handler created we need to add the last little bit to get it ready for use. Open up [GameName]Extension and below private World world; add:
EntityManagerFactory entityManagerFactory;

below the init function add:


    public EntityManagerFactory getEntityManagerFactory() {
        return entityManagerFactory;
    }

and lastly below the request handler for "publickey" add:

        addRequestHandler("login", DBLoginHandler.class);

and at the after all the event handlers add:
        entityManagerFactory = Persistence.createEntityManagerFactory("[GameName]PU");

Please be sure you double check the PU name. You can do this by opening [GameName]Entities and in the META-INF folder open the persistence.xml and look for the persistance unit name and use that name in the quotes.

Thats it for setting up the persistence and retrieving a user from the database and checking to ensure they are a user. You can go further in the if block and check if they are banned or anything else you need to check before you kick the user off the server and provide an error message.

Next time we will cover getting the login message and pushing the user to another scene where they are now "in game"

Unity3D Parse Server Public Key and Send Encrypted Strings

Here we are to finish out our return of the server's public key for this user. Open Unity3D and open our ConnectionHandler class. We need to add in the handlers. Now there is a problem with using blogs in compose mode through blogger. Anything in <> gets chopped off unless you escape it. So first we need to update a few lines. Search for Dictionary in ConnectionHandler, there should be 2 cases of it, the declaration and the initialization in the constructor. Please go in and fix these two places so it says Dictionary<string, ExtensionHandler>
Secondly we need to do the same thing in OnExtensionResponse. Go down to the foreach and it should read:
foreach(KeyValuePair<string, ExtensionHandler> entry in handlers)
With these 2 changes we are ready to receive our "publickey" message from the server. Save the ConnectionHandler and open up LobbyGUI.


In the Awake() function after the smartFox.AddLogListener(LogLevel.DEBUG, OnDebugMessage); add the following line:
handlers.Add("publickey", provider.ServerPublicKeyFromSFSObject);
This adds a handler to our list of handlers so that any time we receive a message with the key "publickey" it will call our provider object, pass it the SFS data and our ServerPublicKeyFromSFSObject will handle reading it and putting it in place. Now we need to create this function. To do so, save this file and open up EncryptionProvider and add the following function after the ClientPublicKeyToSFSObject function:

    public void ServerPublicKeyFromSFSObject(ISFSObject data)
    {
        HasServerPK = true;
        Debug.Log("Got Server Public Key");
        ISFSObject playerData = data.GetSFSObject("key");
        ServerRSA = new RSACryptoServiceProvider();
        RSAParameters param = new RSAParameters();
        param.Modulus = playerData.GetByteArray("mod").Bytes;
        param.Exponent = playerData.GetByteArray("exp").Bytes;
        ServerRSA.ImportParameters(param);


        afterServerPKRecieved();
    }
Just like you saw on the Java side, we take the data, get the object called "key" and ready our the "mod" and "exp" bytes and put them into our key information.


The last thing you will see is a function call to afterServerPKRecieved(). We are creating this as an "event handler". What this means is, just like our handlers in the messaging, we are going to do something when this function gets called. Because we are working with asynchronous messages, there is no guarantee that we will get a key as the first message back from the server. In fact for security you may want to change keys every so often. So when we do get the Key we want to do something. In this case the moment we get a public key back from the server, we want to package up our login and password and send it to the server to be validated. In other scenes we may want to just update our key so we continue to encrypt data correctly. So up in the declarations we want to define the following:

    public delegate void AfterServerPKRecieved();
    public AfterServerPKRecieved afterServerPKRecieved;
again we see a delegate being set up. Now any time we receive a primary key it will call our delegate. Go ahead and save/close EncryptionProvider and go back to LobbyGUI.

In LobbyGUI after our handler is added in the Awake function, lets add the following line:
provider.afterServerPKRecieved += Login_AfterServerPKRecieved;
This tells the compiler that any time we call afterServerPKReceived, we want that function call to call Login_AfterServerPKRecieved(); By using a += you can chain as many functions on to that event as you want as long as it matches the function signature of void FunctionName(); So lets add our new function now. In LobbyGUI add:

    public void Login_AfterServerPKRecieved()
    {
        Debug.Log("sending login");
        ISFSObject tr = new SFSObject();
        tr.PutByteArray("user", provider.EncryptString(username));
        tr.PutByteArray("password", provider.EncryptString(password));


        ISFSObject data = new SFSObject();
        data.PutSFSObject("login", tr);


        ExtensionRequest request = new ExtensionRequest("login", data);
        smartFox.Send(request);
    }
So, now when we get our server's key, we are going to encrypt our login string and password string and send it to the server in an object called login, with a message key of login. The last thing we are going to add today is the EncryptString(string) function so we can send our data encrypted with the server's public key so that people can't see the text we sent along. Save LobbyGUI and open EncryptionProvider. And lets add the last function to today, EncryptString:
    public ByteArray EncryptString(string strToEncrypt)

    {
         System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
         return new ByteArray(ServerRSA.Encrypt(enc.GetBytes(strToEncrypt), false));
    }
All this function does is takes a string, uses our server's public key and encrypts it and passes it back as a ByteArray.

That concludes this chunk. We now receive our server's public key and encrypt data back to the server so prying eyes can't see our login/password.

SmartFoxServer public key return

Next up is the return of our server's public key for the user. Lets start by building our message from the server. Open NetBeans, in our [GameName]Extension project you want to open the [GameName]Account class. We are going to add a couple more properties that we will need later as well as 2 new functions. First is the function I forgot to add when i posted the constructor: readyCipher(). This function takes our private key and sets up the cypher to decrypt all incoming data. First we need a PrivateKey declared, so go up to where you see private PublicKey pubKey; and under it add

private PrivateKey privCipherKey;


Next we need to store off that key while we set up for decryption



    private void readyCipher() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {
        cipher = Cipher.getInstance("RSA");
        KeyFactory fact = KeyFactory.getInstance("RSA");
        privCipherKey = fact.generatePrivate(GetPrivateKey());
        cipher.init(Cipher.DECRYPT_MODE, privCipherKey);
    }

Now that our keys are ready and our cipher is is set to decrypt we need to be able to turn around and send our server's public key for this client back to the client so that the client can decrypt anything we send to them. If you conpare this to our last tutorial you will see that we package up the key the exact same way. We create the "mod" and "exp" keys as ByteArrays from our server's public key. We then wrap that up into another object calling it "key".
    public ISFSObject serverPublicKeyToSFSObject() {
        ISFSObject tr = new SFSObject();

        tr.putByteArray("mod", GetServerPubKey().getModulus().toByteArray());
        tr.putByteArray("exp", GetServerPubKey().getPublicExponent().toByteArray());

        ISFSObject data = new SFSObject();
        data.putSFSObject("key", tr);

        return data;

    }

And the last thing to do from the Java side is to send the public key. Do to this, we open up the World class and go down to our addPlayer function. Between players.add(player) and didAddPlayer = true; we want to add this line of code:
extension.send("publickey", player.serverPublicKeyToSFSObject(), user);
This will tell our extension to get our public key SFSObject and send it to our new player with the message type being "publickey".

Thats it for NetBeans. We have returned our data. Following these last few tutorials will give you the information necessary to send any kind of message. There will be a follow up tutorial on encrypting and decrypting the data, we just won't be covering that today.

Next time we will cover the last portion of our first message, Unity3D's handlers and storing our server public key.

Thursday, December 16, 2010

SmartFox Messaging explained

So it occurred to me that I may have run right past some important information without giving sufficient explanation. I'm going to take some time and explain how the messaging between Unity3D and SmartFox works.

First lets pull up the code from EncryptionProvider. Specifically the ClientPublicKeyToSFSObject function:

public ISFSObject ClientPublicKeyToSFSObject()
{
ISFSObject tr = new SFSObject();

tr.PutByteArray("mod", new ByteArray(ClientRSA.ExportParameters(false).Modulus));
tr.PutByteArray("exp", new ByteArray(ClientRSA.ExportParameters(false).Exponent));

ISFSObject data = new SFSObject();
data.PutSFSObject("key", tr);

return data;
}
The first thing we do is create an ISFSObject. This is the main class for transporting data between the server and client. Next you see we use PutByteArray. We use a Byte Array for for a very good reason. Our Modulus and Exponent are both of type byte[] which is a data type byte array. SFSObjects don't accept byte[] so we must wrap it into a ByteArray class, which in reality is what this already is. The first parameter, however is "mod". This parameter tells our object what to call this piece of data, it is our key. Next we see that we put in the exponent into the "exp" key. The last thing we do is wrap this entire object into another SFSObject. Now some of you may be saying "Why put it into another object when one will suffice?" The reason is pretty simple, if we add more data, like say the time our packet is sent, we want the data for the key to be distinct from the time our packet is sent.

Here is the final structure of key names for our object:
Data
  |
  +-- "key" (SFSObject)
    |
    +-- "mod" (ByteArray)
    |
    +-- "exp" (ByteArray)

This is important for our server side, we now know how the object is structured so we can dig into our object for all the pieces we need. Now lets look at the server side function that takes the data out.

    public void SetPublicKey(ISFSObject data) throws NoSuchAlgorithmException, InvalidKeySpecException {
                ISFSObject keyData = data.getSFSObject("key");
                RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(keyData.getByteArray("mod")), new BigInteger(keyData.getByteArray("exp")));
                KeyFactory fact = KeyFactory.getInstance("RSA");
                pubKey = fact.generatePublic(keySpec);
    }

On the server side, you can see its almost an exact duplicate of the structure above. We first ask for an SFSObject with the string key of "key". We then look inside that object for 2 ByteArray's with keys of "mod" and "exp".

I'm sure many of you can guess that when we get to the next part, we will see the exact same thing. The server will package up the "mod" and "exp" of its public key, and put it in another SFSObject called "key" and send that back to the client.

SmartFoxServer Parse and Store Unity3D public key

Last time we left off we sent our message to the Extension, but it doesn't know what to do with it so nothing happens. Today we will create the information necessary to capture the public, create a public key for the client, and send that back to the client. If any of you have taken a look at the FPS tutorial you will recognize 2 of the classes from that source, World and RoomHelper.

Lets start by creating the packages we will be using. Open NetBeans and right click on our package called [domain.domainname.gamename] and New -> Java Package. We want to create packages handlers, util, and core. Handlers is like the Unity3D handlers, they are classes we have built to handle messages. Util will contain the RoomHelper and any other utility classes that do not fall into other packages. Core will contain anything that is central to our game, in this case World and [GameName]Account.

Lets go ahead and start by creating RoomHelper in the util package. Right click the package and New -> Java Class and call it RoomHelper. It contains the following:


package [domain.domainname.gamename].util;


import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.extensions.BaseClientRequestHandler;
import com.smartfoxserver.v2.extensions.BaseServerEventHandler;
import com.smartfoxserver.v2.extensions.SFSExtension;


import [domain.domainname.gamename].[GameName]Extension;
import [domain.domainname.gamename].simulation.World;


// Helper methods to easily get current room or zone and precache the link to ExtensionHelper
public class RoomHelper {


public static Room getCurrentRoom(BaseClientRequestHandler handler) {
return handler.getParentExtension().getParentRoom();
}


public static Room getCurrentRoom(SFSExtension extension) {
return extension.getParentRoom();
}


public static World getWorld(BaseClientRequestHandler handler) {
[GameName]Extension ext = ([GameName]Extension) handler.getParentExtension();
return ext.getWorld();
}


public static World getWorld(BaseServerEventHandler handler) {
[GameName]Extension ext = ([GameName]Extension) handler.getParentExtension();
return ext.getWorld();
}
}


As you can see, it gets our room information, but we will be using it almost exclusively for getting our world so we can get to our accounts. Since it is referencing World lets go ahead and create it now. Make sure you create it under [domain.domainname.gamename].core
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package [domain.domainname.gamename].core;

import [domain.domainname.gamename].[GameName]Extension;
import com.smartfoxserver.v2.entities.User;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author richach7
 */
public class World {


    private [GameName]Extension extension; // Reference to the server extension
    // Players
    private List<[GameName]Account> players = new ArrayList<[GameName]Account>();

    public World([GameName]Extension extension) {
        this.extension = extension;
    }

    public List<[GameName]Account> getPlayers() {
        return players;
    }

    // Add new player if he doesn't exist, or resurrect him if he already added
    public boolean addPlayer(User user, ISFSObject data) {
        [GameName]Account player = getPlayer(user);
        boolean didAddPlayer = false;
        try
        {
            if (player == null)
            {
                    player = new [GameName]Account(user, data);

                players.add(player);
                didAddPlayer = true;
            }
            else 
            {
                player.SetPublicKey(data);
            }
        }
        catch(Exception e)
        {
            extension.trace(e);
        }
        finally
        {
            return didAddPlayer;
        }
    }

    // Gets the player corresponding to the specified SFS user
    public [GameName]Account getPlayer(User u) {
        for ([GameName]Account player : players) {
            if (player.getSfsUser().getId() == u.getId()) {
                return player;
            }
        }

        return null;
    }

    // When user lefts the room or disconnects - removing him from the players list
    public void userLeft(User user) {
        [GameName]Account player = this.getPlayer(user);
        if (player == null) {
            return;
        }
        players.remove(player);
    }
}
You will see that the world class is the class that links SmartFox's User class to our Account class. We haven't created the Account class so don't worry that you are seeing errors with it missing. We will be creating it shortly. The Account class will be the link to our user and will contain our server's public and private key and will also contain the functions to get our public key from our Unity3D message and create an SFSObject that contains the server's public key for this user. This setup allows our Account to have a unique key making player spoofing impossible. Lets start with the basics for the [GameName]Account class by creating it in the core package.
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package [domain.domainname.gamename].core;

import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSObject;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

/**
 *
 * @author richach7
 */
public class [GameName]Account {

    private User sfsUser; // SFS user that corresponds to this player
    private PublicKey pubKey;
    private RSAPrivateKeySpec privKey;
    private RSAPublicKeySpec serverPubKey;
    private Cipher cipher;
    public User getSfsUser() {
        return sfsUser;
    }


    public [GameName]Account(User sfsUser, ISFSObject data) throws NoSuchAlgorithmException, InvalidKeySpecException {
        this.sfsUser = sfsUser;
        SetPublicKey(data);

        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(2048);
            KeyPair kp = kpg.genKeyPair();
            KeyFactory fact = KeyFactory.getInstance("RSA");
            serverPubKey = fact.getKeySpec(kp.getPublic(),
                    RSAPublicKeySpec.class);
            privKey = fact.getKeySpec(kp.getPrivate(),
                    RSAPrivateKeySpec.class);
            readyCipher();
        } catch (Exception e) {
            //trace(e);
        }

    }

    public void SetPublicKey(ISFSObject data) throws NoSuchAlgorithmException, InvalidKeySpecException {
                ISFSObject keyData = data.getSFSObject("key");
                RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(keyData.getByteArray("mod")), new BigInteger(keyData.getByteArray("exp")));
                KeyFactory fact = KeyFactory.getInstance("RSA");
                pubKey = fact.generatePublic(keySpec);
    }

    public RSAPublicKeySpec GetServerPubKey()
    {
        return serverPubKey;
    }

    public RSAPrivateKeySpec GetPrivateKey()
    {
        return privKey;
    }

    public PublicKey GetPublicKey()
    {
        return pubKey;
    }

    public Cipher GetCipher()
    {
        return cipher;
    }

}
So this class when it gets created is passed a SmartFox User and the SFSObject that contains our Unity3D client's public key. It also goes ahead and generates our server's public/private key pair when we get the client's public key.

Next time we will cover sending a response back to the client and getting that response completing our round trip from client to server and back to client.

Unity3D Public/Private Key

Last time we covered how you can use the SmartFox2.dll to connect to your SmartFox Server. Today we will be covering the setup necessary to use Public/Private Key Encryption. I encourage you to do a little research on it. In short it is a way for our client to wrap up data and send it to the server without others being able to see the content. This is really important for sensitive data like passwords. It isn't as important for things like locations, reloads, firing, etc, however it is a big deterrent for someone trying to spoof packets from other players and either getting them kicked or banned.

This first part is going to be a little complex. We want a way to receive messages from the server that we create. This includes things like retrieving transforms, character information, quest data, really anything that the server would send to the client.

If you have looked through the FPS example, you will see a giant if-else block naming out all the messages we could receive in the OnExtensionResponse function. It's hideous, it's ugly, it's just plain messy. That's not to say its wrong, but we can do better. 

We are going to create a delegate. Delegates are a very complex concept so you can look it up later if you want. To get you started, Microsoft explains it here. We begin by opening ConnectionHandler and inserting this chunk of code between the debug = true and the Awake() function.

    protected delegate void ExtensionHandler(ISFSObject data);

    protected Dictionary <string, ExtensionHandler> handlers;

This creates a list of functions of type ExtensionHandler which we can look up by the string provided as the key.

Next we need to let SmartFox know we what function we wish to use to handle the SFSEvent.EXTENSION_RESPONSE so we need to go into the Awake() function and at the bottom add:
smartFox.AddEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
Again, this tells SmartFox that whenever we receive and extension response, use the OnExtensionResponse function. Extension Responses are any messages we create ourselves. Next we will be creating our OnExtensionResponse. This will look through our dictionary of handlers to try and find a handler that can act on the command passed in by SmartFox.
    // This method handles all the responses from the server
    protected void OnExtensionResponse(BaseEvent evt)
    {
        try
        {
            string cmd = (string)evt.Params["cmd"];
            ISFSObject dt = (SFSObject)evt.Params["params"];

            ExtensionHandler handler;

            if (handlers.TryGetValue(cmd, out handler))
            {
                handler(dt);
            }
            else
            {
                Debug.LogError("Got unhandled cmd: " + cmd);
            }
        }
        catch (Exception e)
        {
            Debug.Log("Exception handling response: " + e.Message + " >>> " + e.StackTrace);
        }

    }
You can see that we get the command, then we get the SmartFox object that we will have been passed from the server. We try to get a handler that is looking for the specific command. If it finds it, it calls it passing in the  object from SmartFox, if it doesn't, we log an error in Unity3d telling us what command was passed to us so we can easily find out what went wrong.

We are now ready to build up our first message to the server, sending our public key. We will start by making a class that will contain functions that allow us to build an SFSObject from our class and another to turn an SFSObject back into data for our class. Start by creating a class called EncryptionProvider in Common\Scripts and open it up to edit it. Like the SmartFox Connection, this will be a singleton.
using System;
using Sfs2X.Entities.Data;
using System.Security.Cryptography;
using Sfs2X.Util;
using UnityEngine;

public class EncryptionProvider
{
    RSACryptoServiceProvider ClientRSA;
    RSACryptoServiceProvider ServerRSA;

    // Singleton pattern
    private static EncryptionProvider provider;
    public static EncryptionProvider GetInstance()
    {
        if(provider == null)
        {
            provider = new EncryptionProvider();
        }
        return provider;
    }


    public bool HasServerPK
    {
        get;
        protected set;
    }


    private EncryptionProvider()
    {
        ClientRSA = new RSACryptoServiceProvider();
        HasServerPK = false;
    }

}
This is our base class we will use to store the server public key and our public and private keys. In the constructor we create our Encryption provider and we set a flag to tell us if we have the server's key yet. Now we will add the function that creates an SFSObject from our ClientRSA:
public ISFSObject ClientPublicKeyToSFSObject()
{
ISFSObject tr = new SFSObject();
tr.PutByteArray("mod", new ByteArray(ClientRSA.ExportParameters(false).Modulus));
tr.PutByteArray("exp", new ByteArray(ClientRSA.ExportParameters(false).Exponent));
ISFSObject data = new SFSObject();
data.PutSFSObject("key", tr);
return data;
}
In order to recreate a key we need the modulus and exponent of the public key. By passing false to ExportParameters we get the public key, if we were to pass true, we would get the private key.

Now lets send it to the server. Open ConnectionHandler and lets add the provider and the code to get it set up. Under the handlers declaration add:
    protected EncryptionProvider provider;
At the bottom of the awake function add:
        provider = EncryptionProvider.GetInstance();
Now open up LobbyGUI and go to the OnLogin function. In the empty else block add:
            ExtensionRequest request = new ExtensionRequest("publickey", provider.ClientPublicKeyToSFSObject());
            smartFox.Send(request);
Congratulations, you just sent your first message to the server. Next time we will modify the [GameName]Extension code to receive the message and send back the server's public key.

Wednesday, December 15, 2010

Unity3D Login and Joining a Room

First of all a big thank you to those who have been patient all this time. Its been a long time just to get to the part where we are going to create our login call from Unity3D into SmartFoxServer. Having said that lets just dive right in.

In Unity create a new C Sharp Script under Lobby\Scripts and lets call it LobbyGUI.

using UnityEngine;
using System.Collections;
using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Logging;
using Sfs2X.Requests;
using Sfs2X.Entities.Data;
using Sfs2X.Util;


public class LobbyGUI : ConnectionHandler
{
}


Hopefully you noticed right away that we put ConnectionHandler instead of MonoBehavior. With this simple change, all that code we covered last time is now part of this class. If you aren't familiar with coding, this is something called Inheritance. I won't cover that here, please look it up for yourself. Now to add the code. We will start with the variables this class will use:



    public string serverIP = "127.0.0.1";
    public int serverPort = 9933;
    public string gameZone = "[GameName]";


    private string username = "";
    private string password = "";
    private string loginErrorMessage = "";
    private string lastModMessage = "";
    private bool debugMessages = false;


This list of variable is pretty self explanatory. The only confusing thing might be the lastModMessage which will contain the last message passed by the moderator. We will use this later to show the user what happened if they were kicked from the server. Remeber to update the gameZone with your game's name.

Next up is our awake call. Here we will register for all the SmartFox Messages that might be passed to us:

    new void Awake()
    {
        base.Awake();
        // Register callback delegate
        smartFox.AddEventListener(SFSEvent.CONNECTION, OnConnection);
        smartFox.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
        smartFox.AddEventListener(SFSEvent.LOGIN, OnLogin);
        smartFox.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
        smartFox.AddEventListener(SFSEvent.LOGOUT, OnLogout);
smartFox.AddEventListener(SFSEvent.MODERATOR_MESSAGE, OnMod);
        smartFox.AddLogListener(LogLevel.DEBUG, OnDebugMessage);

    }

You will notice that we call base.Awake(). This is so that when this object's awake call is made, it calls ConnectionHandler's Awake function also which will make sure we are ready to use our SmartFox connection. If you were to try and run the code now, however, you will get a bunch of errors talking about missing functions. We will cover those shortly. First we are going to get our GUI into place. You can play with this if you want, but I'm just going to give it to you. I'll leave it up to you to use Unity to figure out what it does. Also you could watch the tutorials over at BurgZerg Arcade.

    void OnGUI()
    {


        //GUI.skin = gSkin;


        //GUI.Label(new Rect(2, -2, 680, 70), "");


            // Login


            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), lastModMessage);
            GUI.Label(new Rect(10, 255, 400, 100), loginErrorMessage);


            if (GUI.Button(new Rect(100, 165, 100, 25), "Login") || (Event.current.type == EventType.keyDown && Event.current.character == '\n'))
            {
lastModMessage = "";
                smartFox.Connect(serverIP, serverPort);
            }
            if (GUI.Button(new Rect(100, 195, 100, 25), "Logout"))
            {
                UnregisterSFSSceneCallbacks();
            }
    }


So now we are set and ready to put in those missing functions. I will describe what each of the are doing. First is OnConnection. Here we check if we successfully connected and if we did we send the username to the server and join the General room.
    public void OnConnection(BaseEvent evt)
    {
        bool success = (bool)evt.Params["success"];
        string error = (string)evt.Params["error"];

        if (success)
        {
            loginErrorMessage = "";
            if(debug)
            {
                Debug.Log("Connected");
            }
            smartFox.Send(new LoginRequest(username, "", gameZone));
            smartFox.Send(new JoinRoomRequest("General"));
        }
        else
        {
            loginErrorMessage = error;
        }
    }
Next we will put in the code that happens when we get disconnected.
    public void OnConnectionLost(BaseEvent evt)
    {
        loginErrorMessage = "Connection lost / no connection to server";
    }

    public void OnDebugMessage(BaseEvent evt)
    {
        string message = (string)evt.Params["message"];
if(debugMessages)
{
Debug.Log("**** DEBUG ****" + message);
}
    }
I also included the code that happens when you turn on message debugging. On a disconnect we just put up the connection lost message. When a debug message comes it we simply print it out. This can be useful if you are getting errors but don't know what message is coming in and causing problems.

So now we set up our connection lets get the function in place for when we successfully log in to our zone. This function's purpose is to tell us if we successfully logged in or not.
    public void OnLogin(BaseEvent evt)
    {
        if (evt.Params.ContainsKey("success") && !(bool)evt.Params["success"])
        {
            // Login failed - lets display the error message sent to us
            loginErrorMessage = (string)evt.Params["errorMessage"];
            Debug.Log("Login error: " + loginErrorMessage);
        }
        else
        {
            // We logged into the main game successfully
        }
    }
We left the else block blank. You can play around with this area. You could load the next level or you could set up public/private key encryption, or any number of other things. Lets go ahead and get in the code that happens when an error occurs during login.
    public void OnLoginError(BaseEvent evt)
    {
        Debug.Log("Login error: " + (string)evt.Params["errorMessage"]);
    }

    void OnLogout(BaseEvent evt)
    {
        Debug.Log("OnLogout");
    }
I just put in some simple debugging. There really isn't anything to do in these functions as far as the tutorial is concerned. You could have functions in OnLogout that take you back to the Lobby Scene, but since we are here already, it really isn't useful. The last function we will cover is the OnMod. We use this function to display a message to the user if a Moderator sends us a message.
    void OnMod(BaseEvent evt)
    {
        if(evt.Params.ContainsKey("message"))
        {
            lastModMessage = (string)evt.Params["message"];
        }
    }
That about covers it for this chunk. We now can run our unity program and be logged into our SmartFox server admin panel and see the user as they join the [GameName] extension and then join the General Room. Next time we will cover sending the client's public key and getting back the server's public key so we can use private/public key encryption to send our username and password to the server to look up against our SfGuardUser.