Tuesday, July 17, 2012

Unity Scripts & Network Management

Hi. Today I'm going more in depth with Unity development by adding some important information for managing you network and players. SmartFox connections can be very delicate, and a program that closes without properly disconnecting from SmartFox will crash Unity nearly every time.

First, here a few simple tips for designing the script that connects you to SmartFox. This pertains to people who debug on a localhost and also on a remote connection. Save your remote connection and 127.0.0.1 in strings and then add a public bool variable so that your program will switch between Local and Remote Connection in Start. Then you can tick the checkmark in Unity's inspector whenever you want to switch between your different SmartFoxServer instances. You can do something similar with a guest boolto change the user name to guest if you have allowed a guest to log into your server. Lastly, be sure that debugging information is always sent on bad connections. In my connector, I have a status message that is updated based upon how things are going with the three phases: Connection, Login, and Joining the Room. After logging in, I show the client a list of each of the rooms in the zone, so that they can choose which one they want to join. A client should be alerted of the status of all these things as they go along.

Make sure your database is configured the way you want it and that your zone and room connection scripts should be connecting to it as you intend. You can sett a zone variable using the SmartFoxServer admin tool to allow guests to log in. Their name will  always start with Guest. You could keep this disabled and manually assign a name, but I find this automatic way to be much simpler. In your Unity Script that connects to the server, you need to send a keyword in your login request.  I'd suggest setting the user name to "Guest" or "" (null), and then in your zone extension->OnLoginHandler add in the code below to avoid triggering an error message for a login request that intentionally will not be in the database.

if(clientName.compareToIgnoreCase("Guest")==0 
    || clientName.compareTo("")==0)
{
    //Guest login code
} 
else try 
{
    //Your Normal Login Code;
}

In my game before each user is sent the reply for the "spawn me" request, my room extension retrieves a specific model name which is stored in the database. You'll have to ensure that a default model name is returned for guests. In your room's "Spawn Me Handler",  I'd suggest doing this without querying the database so that your team can typically run the game without having to start WAMP or have the database locally installed.

if(user.getName().startsWith("Guest"))
{
    model="Guest"
} 
else try 
{
    //db query...
}

In your Unity Player Manager, check for this and load a generic prefab, or you could even generate a random model if you are up to the challenge.

There are a lot more things you should do to your Unity code. Now that we have discussed adding a player, let's make sure that your game knows how to remove one. Make sure that your users have a way that they can request to log out of the zone and disconnect from the server. The room extension that you built should have this:
addEventHandler(SFSEventType.USER_LEAVE_ROOM, OnUserGoneHandler.class);

However, it's come to my attention that the USER_LEAVE_ROOM event is not called when you send a logout request to the server. A logout only occurs in the zone, so your room will not be notified. This will cause a lot of problems because you have to access your world class and remove the leaving player so that their model is removed from the list of players and from each client's instantiated guests. Do this when a user activates logout. I'd also suggest placing this inside of OnApplicationQuit in SmartFoxConnection before the smartFox.disconnect. That way the users will be gracefully removed even if they don't log out. There is a lot of messaging among users, so make sure to broadcast a leaving message.
smartFox.Send (new PublicMessageRequest(userName + " left"));
smartFox.Send (new LeaveRoomRequest());
smartFox.Send (new LogoutRequest());

Look here for more information on leaving the room. http://www.smartfoxserver.com/forums/viewtopic.php?t=12524

I find it necessary to store my id within network manager so that if it happens to be me that is logging out, I don't erroneously try to remove my id from the guests dictionary.
// When a user leaves room destroy his object
private void OnUserLeaveRoom(BaseEvent evt)
{
 User user = (User)evt.Params["user"];
 if(user.Id != smartFox.myself.Id)
 {
      PlayerManager.Instance.RemoveGuest(user.Id);
 }
 Debug.Log("User "+ user.Name+ " left");
}

Then place this in your Player Manager.
public void removeGuest(int id)
{ 
    receivers.Remove(id);
    Destroy(guests[id]);
    guests.Remove(id); 
}

Saturday, July 7, 2012

Link SFS to a MySQL database using JDBC

Most admins in the SmartFox forums seem to think that connecting to a database is not that complicated, but I came across several hours worth of problems while trying to make it work. Most of these are from coding subtleties that only a person truly versed in JDBC knows. It's my goal here to help you connect to your database with ease. I know that everyone has their own formula for database management (usually the first thing you learn is what you want to use), and it shouldn't be too difficult to follow my example in mind of exchangeable parts. Here is a quick bio of why I'm using JDBC (skip to next paragraph if you don't care). I used it once in my graduate Database Management class at USC. In that class, my teacher taught us the theory, but we had to learn all of the implementation on our own to finish his rigorous assignments. I developed a formula to test all of the MySQL, PHP, HTML, XML Schema, CSS etc! No joke, I had to pull an all nighter every time one of our a coding assignment was due. So, I became extremely good friends with the amazing w3 school tutorials, occasionally reading an entire web language tutorial multiple times. http://www.w3schools.com/ should be your first reference for any web development tutorials. Even with my prior exposure to database management (I'm in no way a guru), I still had trouble making things work with SmartFoxServer.

I am currently managing the server on my own computer using WAMP. I believe Mac users can use MAMP.  This program allows you to manage a local database on your computer and even expose it to the outside world if you so desire. It comes built in with MySQL which is the component that you need for this tutorial. Instead of using PHP or ASP like you would do when developing a web site, we're going to connect to the database with JDBC: get the connector at http://www.mysql.com/products/connector/ and place the .jar in your {SFS2X directory}/Extension/__lib__.  I have created a MySQL database on my local computer using the phpMyAdmin tool and Oracle's MySQL Workbench. You can create a .sql file pretty easily with the Workbench. Design your database visually and use the forward engineer function to make a .sql creation script. Then, create that database on your WAMP server by typing localhost in your browser and clicking phpMyAdmin. You might need to edit your hosts file so that localhost will redirect to 127.0.0.1, and if you already have other programs in control of host, try associating it with a name like localhostwamp. This will show you all the databases you have on your server. Create a new database with the file that you built with the workbench.

Ok, so now you have installed a server that is running MySQL, and you have created a database that at least holds one user entry with a user name and password. You'll want to combine the official instructions http://docs2x.smartfoxserver.com/GettingStarted/howtos#item2
with this tutorial from the forums http://www.smartfoxserver.com/forums/viewtopic.php?f=4&t=11793&p=48400&hilit=jdbc+databasemanager#p48400  Well there are a couple of things that they don't explain. Firstly, the Database driver class doesn't refer to the name of the .jar connector, it refers to the name of the class located inside of the file. So for JDBC it's com.mysql.jdbc.Driver  If you're using WAMP then your MySql server should be running on port 3306. You can check this by clicking WAMP -> MySQL -> my.ini which will show you your password and port. The default user name is root. So for connection string it is jdbc:mysql://127.0.0.1:3306/yourDbName
Change the TestSQL value to "select count(*) from yourUserTable". When you boot up your server, I suggest doing it manually by clicking the SFS2X.bat file in your SFS2X directory.  This will show you a cmd window and alert you of any problems it is having connecting to the database. If it doesn't show any errors, then that means that your TestSQL script worked! Be happy! If not, then perhaps your sql statement isn't following standard syntax or has a typing error. Try getting some help from the post in the forums.

Now that your server is configured to connect with the database, you can start coding your server side extension. Previously, I've showed you how to create a room extension, but only a zone extension can be used to register login events. You'll need to open Net Beans and create a new project. Refer to my other posts about how to create an extension. In the main class of this extension,  you'll want to add this line of code addEventHandler(SFSEventType.USER_LOGIN, LoginEventHandler.class); 
inside of your init function. Make sure to create a Login Event Handler class for that function to call. There is a great snippet of code where someone has made a handler that checks the user name and password against the values in the database. http://www.smartfoxserver.com/forums/viewtopic.php?t=13292 Go with the second portion of code that says
by Zageron» 30 Jan 2012, 16:40  You can reference most of the functions in this documentation by SmartFoxServer and searching for the IDBManager http://docs2x.smartfoxserver.com/api-docs/javadoc/server/

It is a pretty complex thing, but this will work really well and follows pretty good Java coding practices. There are a couple things that might seem strange. One is that he calls the password a hash. This is true. SmartFoxServer will always send a password from the client as a hash. So that is why you have to use the API function to check the password from the database against the one the client sent. The other has to to do with stored procedures. You can't just grab a value from result. You must specify Result.first() or Result.next() to get increment the result iterator for the user's table entry for which you were looking. Prepared statements are good because you can iterate through several rows that are returned each time with Result.next(). Still having problems? Check this article on prepared statements to see why he is including a ? in the code. http://docs.oracle.com/javase/1.4.2/docs/api/java/sql/PreparedStatement.html  
 and then look here http://stackoverflow.com/questions/2120255/java-resultset-exception-before-start-of-result-set

*make sure to enable use custom login for the zone

Here is a little update so that you will know how to proceed when you want to connect to the database from the Room instead of the Zone extension. I do this to load a specific character according to what my user has stored in the database. Since you're following the example of the first person shooter, you should have a world class and a spawn me handler. This is quite a nice gift, so enjoy and make your program connect to your database like a champion!

//This is in your SpawnMeHandler.java class
public void handleClientRequest(User user, ISFSObject data)
{
 World world = RoomHelper.getWorld(this);
 String modelName = null;
 String request = "SELECT * FROM users WHERE username = ?";
 try
 {
  modelName = venue.sqlRequest(request, "characterModel", user.getName());
 }
 catch(Exception ex){}
 
 boolean newPlayer = venue.addPlayer(user, modelName);
 if(newPlayer)
 {
  sendOtherPlayersInfo(user);
 }
}

//Connect to a database in your Room Extension
public String sqlRequest(String sqlRequest, String field, String userName) throws SFSException
{
 IDBManager dbManager = extension.getDbManager();
 try
 {
  Connection connection = dbManager.getConnection();
  PreparedStatement stmt = connection.prepareStatement(sqlRequest);
  stmt.setString(1, userName);
  ResultSet result = stmt.executeQuery();
  if(result.first())
  {
   sqlRequest = result.getString(field);
  }
  
  stmt.close();
  connection.close();
 }
 catch(Exception ex)
 {
  SFSErrorData errData = new SFSErrorData(SFSErrorCode.GENERIC_ERROR);
  errData.addParameter("SQL Error: " + ex.getMessage());
  throw new SFSLoginException("A SQL Error occurred: " + ex.getMessage(), errData);
 }
 
 return sqlRequest;
}

Thank you for reading, and feel free to comment below.

Sunday, July 1, 2012

Unity Scripts & Game Objects

Unity is a powerful game engine that caters to the needs of programmers of any skill level. Its ability to create object prefabs and graphically place them using the Unity Editor is arguably the most used feature. There is another way, however, to place objects in a scene: via a script. In this post I'll discuss how to use Smart Fox Server and Unity to create a player object and update there locations.

If you've been reading this blog from the first post, then you already have a basic understanding of how to develop an infrastructure for your multiplayer game. You've looked closely at the First Person Shooter example for Unity available here http://docs2x.smartfoxserver.com/ExamplesUnity/fps and you understand that by creating a custom extension you can allow the server to correspond with your game.

To get things running, attach Network Manager and Player Manager as components of the main camera in your game scene. You should have a login scene that that connects the user to the zone and room, and that scene will then save the connection into the static class SmartFoxConnection and load your game scene.

Most games that you have developed probably have the player already added into the scene. The challenge of creating a networked, multiplayer game is that no character should already be in the scene. Rather, when the Unity client starts the game scene, it sends a request to the server extension to create that player on their client and every other client connected to the same room.

In my previous post I discussed the different classes that you will need to develop in Unity. Another important aspect is to create player prefab objects. For the most basic implementation, you'll want two kinds of objects in Player Manager. Create two prefabs: myPlayer and otherPlayer. Add the character controllers and animations as you normally would to a character. On the otherPlayer prefab, make sure to remove any code that would respond to input from the user's keyboard. Instead, you want to add a Network Transform Receiver script onto that prefab.  Then, add NetworkTransformSender.cs to the otherPlayer prefab. You'll need to have this code in your Player Manager and save.
public GameObject playerPrefab;
public GameObject guestPrefab;
Then, look at your main camera in the Unity property inspector. You'll see these two objects by the Player Manager script that you added to the camera. Click the small circle to the right of playerPrefab and select your myPlayer prefab. Then, do the same for guestPrefab except select the otherPlayer prefab. Use any naming convention you like, just make sure it's obvious which is which.

Make sure you've followed the guidlines of the FPS demo and enabled UDP. The game sends user transforms with UDP because it is much faster and, though less reliable than TCP, UDP will function well enough for sending transforms.

//SendTransform is a function in NetworkManager.cs
public void SendTransform(NetworkTransform nTransform)
{
 Room room = smartFox.LastJoinedRoom;
 ISFSObject data = new SFSObject();
 nTransform.ToSFSObject(data);
 // True flag = UDP
 ExtensionRequest request = 
  new ExtensionRequest("sendTransform", data, room, true);
 smartFox.Send(request);
}

Let's explore the code in the picture above. Send Transform is called in the SendTransformHandler after each period of time as set by the sendingPeriod variable. When called, it determines the client's current room and serializes the transform in an ISFSObject (used to efficiently transmit data across the network). Then it creates a new Extension Request which it labels "sendTransform". You might recall the init function in your extension.java file has the line addRequestHandler("sendTransform", SendTransformHandler.class) So, when this request reaches the server the SendTransformHandler.class is called and will in turn send a request back to all the other clients.

Adding the data variable to request is self-explanatory. The room specifies the exact room to target, however, it also limits your extension to only function as a room extension and NOT as a zone extension (otherwise to transmit to the zone, replace room with false). Last but not least is the value true. This means that this request will be transmitted using UDP.  So make sure that somwhere in the login script that connects to your server you have called this function:  smartFox.InitUDP(serverName, serverPort) or the UDP request will not transfer.

Finally, in Player Manager you will need to maintain a dictionary of all the otherPlayer Game Objects and Network Transform Receivers in order to manage each of their locations.Once you've created them, you'll want functions like these in your Player Manager class. Creating your SpawnPlayer class does not require a dictionary, and I will leave creating it as an exercise for you to figure out on your own. One thing I haven't figured out yet is how to have the main camera target the transform of your player object after it has been created.  But, this is how you can manage all the other players.

public void SpawnGuest(int id, NetworkTransform nTransform, string name)
{
 GameObject guestObj = GameObject.Instantiate(guestPrefab) as GameObject;
 guestObj.transform.position = ntransform.Position;
 guestObj.transform.localEulerAngles = ntransform.AngleRotationCC;
 
 guests.Add(id, guestObjt);
 receivers.Add(id, guests[id].GetComponent());
}

public NetworkTransformReceiver GetRecipient(int id)
{
 if(guests.ContainsKey(id)
 {
  return receivers[id];
 }
 
 return null;
}

Feel free to leave me a comment or contact me through www.GuitarRpg.com
This past week I've been coding and debugging the client classes in Unity that will connect to the Smart Fox Extension that I coded for my previous post. Throughout the process I realized how expansive, out of control, and volatile a project can get up until you sort out the problems. The Smart Fox forums were the best source of information, but the information you need is difficult to find and even more difficult to validate. I'd like to explain it here to keep you from having to trudge through the forums as much as I did.

The first thing to do is to decide which Unity classes from the First Person Shooter example would help accomplish one seemingly simple goal. I'll state the goal. "I want smart fox server to respond to a spawn Me request from a Unity client. The server will send a transform (location coordinates) and character prefab name for the client to load into the scene after I log into the room." That was the primary goal, but I also coded beyond that to broadcast a message to all other players in the room so that each client will render every player in the same room.

Previously I used Eclipse to create 3 classes: Connector, ChatWindow, and SmartFoxConnection. Now, to interact with my extension, I've improvised on these other classes from the FPS example: Network Manager, Player Manager, Network Transform, and Network Transform Receiver/Sender. After some trial and error, I decided that Net Beans with Java SE was a much better IDE for this kind of development. It is easier to create a Jar and to attach a debugger with Net Beans.

Once you have set up your project in Net Beans, you can edit the build.xml file to automatically copy your jar by adding these lines of code. (replace the ** items with the actual paths and folder names)



    Builds, tests, and runs the project MyExtension.
    
    
        
            
        
 



Now that you have a .jar use the Smart Fox administrator tool to load the file into your room. I've noticed that when you use a line like "ExtensionRequest request = new ExtensionRequest("spawnMe", new SFSObject(),smartFox.LastJoinedRoom)" it will only work if you add the extension to the room and NOT the zone. Look to the forums for guidance. Adding an extension to the zone is more efficient if you have several rooms. But, for now, you are only trying to get the extension to work within one room in a particular zone.

It's time to debug your extension. It is ALWAYS best to make sure the stuff works on your localhost first. Leave the extension's project open in NetBeans. Using the admin tool go to server configurator and add 127.0.0.1 TCP port 8787 to the allowed incoming connections. Then, you should save your sfs2x.bat in C:\Program Files\SmartFoxServer2X\SFS2X as sfs2x_debug. Using notepad++ or any text editor, the contens of sfs2x_debug should be:

@java -cp "./;lib/*;lib/Jetty/*;extensions/__lib__/*" -Xdebug -Xnoagent 
-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n 
-Dfile.encoding=UTF-8 com.smartfoxserver.v2.Main $1 $2 $3

You don't "have" to use 8787, that's just what was suggested in the forums. If you're running a smart fox server, then stop it, and run this .bat instead. It will load a smart fox server that is perfectly capable of debugging with Net Beans or Eclipse (though I suggest Net Beans). Click Debugger->Attach Debugger (after you've started the sfs2x_debug.bat. When you run the project in Unity (with Net Beans project open and attached) then at the break points you've set in net beans, you can see the values of your objects and enjoy the freedom given to you by a debugger. You can also run Unity in debug mode at the same time. A debugger has 3 important tools that look like arrows. Step Over (executes a line of code and all it's internal functions), Step Into (shows you the line of code that is being called: very good for stepping through code that you don't exactly understand), and Step Out (exit the complicated inner workings of code). Use the debugger as much as you can to help avoid hours of searching for errors. I also suggest that in your Unity .cs files you add to your Debug.log ("message") at points in the code you want to make sure are being executed. If you don't see the messages in your Unity console, then you know that that line of code has not been called properly.

These debuggers are the absolute best way to help you code your extensions. If you have any questions, contact me through www.GuitarRpg.com and I'll try to help you out.

Running and Debugging a Room Extension

This past week I've been coding and debugging the client classes in Unity that will connect to the Smart Fox Extension that I coded for my previous post. Throughout the process I realized how expansive, out of control, and volatile a project can get up until you sort out the problems. The Smart Fox forums were the best source of information, but the information you need is difficult to find and even more difficult to validate. I'd like to explain it here to keep you from having to trudge through the forums as much as I did.

The first thing to do is to decide which Unity classes from the First Person Shooter example would help accomplish one seemingly simple goal. I'll state the goal. "I want smart fox server to respond to a spawn Me request from a Unity client. The server will send a transform (location coordinates) and character prefab name for the client to load into the scene after I log into the room." That was the primary goal, but I also coded beyond that to broadcast a message to all other players in the room so that each client will render every player in the same room.

Previously I used Eclipse to create 3 classes: Connector, ChatWindow, and SmartFoxConnection. Now, to interact with my extension, I've improvised on these other classes from the FPS example: Network Manager, Player Manager, Network Transform, and Network Transform Receiver/Sender. After some trial and error, I decided that Net Beans with Java SE was a much better IDE for this kind of development. It is easier to create a Jar and to attach a debugger with Net Beans.

Once you have set up your project in Net Beans, you can edit the build.xml file to automatically copy your jar by adding these lines of code. (replace the ** items with the actual paths and folder names)



    Builds, tests, and runs the project MyExtension.
    
    
        
            
        
    



Now that you have a .jar use the Smart Fox administrator tool to load the file into your room. I've noticed that when you use a line like "ExtensionRequest request = new ExtensionRequest("spawnMe", new SFSObject(),smartFox.LastJoinedRoom)" it will only work if you add the extension to the room and NOT the zone. Look to the forums for guidance. Adding an extension to the zone is more efficient if you have several rooms. But, for now, you are only trying to get the extension to work within one room in a particular zone.

It's time to debug your extension. It is ALWAYS best to make sure the stuff works on your localhost first. Leave the extension's project open in NetBeans. Using the admin tool go to server configurator and add 127.0.0.1 TCP port 8787 to the allowed incoming connections. Then, you should save your sfs2x.bat in C:\Program Files\SmartFoxServer2X\SFS2X as sfs2x_debug. Using notepad++ or any text editor, the contens of sfs2x_debug should be:

@java -cp "./;lib/*;lib/Jetty/*;extensions/__lib__/*" -Xdebug -Xnoagent 
-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n 
-Dfile.encoding=UTF-8 com.smartfoxserver.v2.Main $1 $2 $3

You don't "have" to use 8787, that's just what was suggested in the forums. If you're running a smart fox server, then stop it, and run this .bat instead. It will load a smart fox server that is perfectly capable of debugging with Net Beans or Eclipse (though I suggest Net Beans). Click Debugger->Attach Debugger (after you've started the sfs2x_debug.bat. When you run the project in Unity (with Net Beans project open and attached) then at the break points you've set in net beans, you can see the values of your objects and enjoy the freedom given to you by a debugger. You can also run Unity in debug mode at the same time. A debugger has 3 important tools that look like arrows. Step Over (executes a line of code and all it's internal functions), Step Into (shows you the line of code that is being called: very good for stepping through code that you don't exactly understand), and Step Out (exit the complicated inner workings of code). Use the debugger as much as you can to help avoid hours of searching for errors. I also suggest that in your Unity .cs files you add to your Debug.log ("message") at points in the code you want to make sure are being executed. If you don't see the messages in your Unity console, then you know that that line of code has not been called properly.

These debuggers are the absolute best way to help you code your extensions. If you have any questions, contact me through www.GuitarRpg.com and I'll try to help you out.