The new C# Bunnies server is now running in place of the old PHP version. Also out is the new version 2.1.5 client which adds in multiplayer support. If you have a Bunnies account and log in with it you will be able to see and chat with other players in the same map as you. Enemies are controlled by the client so if there are any bunnies or soldiers running around you’ll need to take care of them.
One of the next features to add is custom player sprites. But first I think I’m going to focus on making some actual levels and updating the manual.
One of the biggest memory hogs for Bunnies is all the textures that sprites use. So I switched things up a bit. The first thing I changed is that texture handlers now only load their textures if they are actually used. The second change is that objects now reference a texture handler.
The first change means that even though the game supports and knows about X sets of textures, it won’t actually load those sets until an object that references the set has a request for a texture. So the first time a texture handler is actually used the sprites all load. After that it just returns the requested texture.
The second change means that you can use an SS texture set with a Guard object.
The reason for this change is to support custom sprites for players. A default sprite set will be created for players for now. In the future the Bunnies website will support uploading and creating new sprite collections for your account. The Bunnies client will then be updated to be able to make use of these custom sprite collections.
.Net Memory Profiler (not free, has non-crippled trial period) is a nifty little tool that lets you see where the memory usage for your .Net app is coming from. I of course used it on Bunnies because it was consuming well over 300MB of memory. Turns out that the Point Class was responsible for about 224MB of that.
The point class is used by every pixel of every texture for lighting calculations. It’s the bump map and depth map values for every pixel of the texture. According to the profiler each instance of the point class consumed 48 bytes of memory and over 4.5 million of them were in memory.
The point class consisted of 5 doubles and some methods to do basic vector math. 5 doubles is 40 bytes and the additional 8 bytes I assume keep track of where the methods are in memory. In Perl land “self” is passed into class methods which is good to know. It doesn’t matter how many methods your class has, all the methods are in memory once and self is implicitly passed in. So the methods consume memory only once and only the instance variables consume memory for each instance in memory.
Anyway, now you know how that works. Since the bump and depth maps are dealing with approximating light and high precision values are not needed, I switched the five doubles to five floats. That cut down the size of each instance to 28 bytes. 4.5 million 48 byte objects in memory results in 205MB of memory usage. 4.5 million 28 byte objects in memory results in 120MB of memory usage. 58% of the original size.
To further illustrate the absurdity of this, consider that it only takes 3 bytes to represent the RGB values of each pixel of the texture. 4.5 million RGB values take up only 13MB of memory. So the vast majority of the memory usage for Bunnies comes from lighting information.
The added bonus of switching to floats (which didn’t cause any noticable decrease in lighting effects quality) is that the game loads faster and I got about a 5fps speed boost. Up to 42fps from 37. A 13.5% increase.
The new Bunnies server will be running this weekend and the new client should be available for download then as well. I’ll be tweaking the position update frequency and playing around with other server things before I commit to a release.
It took about 5 hours of work to translate the existing PHP based server into a C# based server. That may say something about my coding abilities or the simplicity of the server.
Just to test I ran the PHP server and two instances of Bunnies on the same Dual Core 2Ghz system with 2GB of RAM. It handled just fine with position updates being sent every tenth of a second from each client. A single core 1.7Ghz system with 896MB of ram couldn’t handle it. We’ll see later how that system handles with the C# based server.
One of the historical features of game servers that I’ve written is that they use double buffering. What that means is that data coming in is simply stored. It’s not processed right away. Data going out is also simply stored. Once per iteration of the main loop all the stored data is processed and outgoing messages are generated. Once all the incomming messages have been processed then the outgoing messages are actually sent out on the line.
The reason for this is simple: TCP is a stream based protocol. Trying to treat it like a message based protocol is going to introduce software induced lag. Not only is it a stream based protocol but it has an algorithm built into it which causes messages to be held by the network card until certain conditions are met (one being the amount of data) and then the data actually gets sent which cuts down on overhead. Every TCP packet that goes out has a header tacked on that is several bytes. So it’s in your best interest to not force multiple packets when one would suffice.
You may be thinking, “why not use UDP?” Because it’s unreliable and unless your an expert at UDP you’re going to end up with performance no better (and possibly worse) than TCP. TCP is very fast if you know what you’re doing. Packaging multiple game message into one TCP packet is the easiest way to boost performance significantly.
No matter what your server is doing it has to create the messages. You can’t save time there except by simplifying message generation. But you can save a lot of processing time by sending at most one message per connection per iteration instead of many. If it takes N seconds to send a message it will take 5 * N to send five messages. If it takes N seconds to send a 10 byte message it will not take 2 * N to send a 20 byte message. It will still take very close to N seconds.
The next trick of course is to make your main loop really fast. The amount of time it takes to read in a message, process it and send out a message is the amount of software induced lag. So if it takes 0.25 seconds to process a text message and forward it off to all the clients then that 0.25 seconds of lag you just created.
The game server should be running thousands if not millions of iterations per second under light load. As more people connect and more messages come in that is going to start to drop. If your game loop drops to say 10 iterations per second that means your server is adding 1/6 of a second in lag which is going to be noticable.
That’s the biggest reason for going to C#. The Bunnies server needs to be able to handle large numbers of players simultaniously. PHP may do fine for light loads but there’s no reason to keep developing it knowing it won’t scale.
After my initial look into setting up sockets and MySQL in C# I realized pretty quickly this wasn’t a difficult task.
To use MySQL with C# you simply use MySql.Data.MySqlClient which is available after you install MySQL Connector .Net provided by MySQL.org Here.
Warning: It’s licensed under the GPL.
And since you have to link it to your program your code is now GPL. This is why they call the GPL a viral license and why I don’t support it.
To get around this you can use the
MyODBC connector. This allows you to create a MySQL ODBC connection through your Control Panel->Administrator Tools->Data Sources tool in Windows.
This gets around the GPL issue with your code since your program never links in any GPL code. Windows handles everything.
When working with multiple environments it’s easier to use the ODBC connector than the linked library since you can keep the ODBC name the same on all systems and not have to worry about changing settings when you copy your program from one system to another.
I started with the first option but after digging around a bit more it’s obvious it’s better to use the ODBC connector. The Bunnies client is open source under the BSD license (essentially public domain). The Bunnies server is not open source.
One of the things to keep in my with the ODBC connector is that named parameters are not supported. Use “?” as place holders in your query and then add the parameters in order. You can use “AddWithValue” and the name parameter will just be ignored. Leave it blank or use some text that will help you remember what it’s for so you order your parameters correctly.
You may or may not know that the Bunnies game server is written in PHP. The reason for that choice was simple: I didn’t care to figure out how to get C# to work with MySQL. That and I could just copy existing PHP code from the web-site and use it in the game server. It may be the fact I’m trying to run the game server and two game instances on the same 1.7Ghz system but I’m willing to wager that PHP is not cut out for multiplayer game servers.
I had to cut down position,angle,velocity notifications down to half second intervals just to keep the server from choaking on the messages. One thing is certain though, the Bunnies client supports multiplayer. The next step is to rewrite the game server in C# so it can support more than 2 players at the same time.
Initially all other game entities will be handled on the client side. So player 1 and player 2 will see each other and get each others messages if they’re in the same map but they’ll be going after two separate sets of enemies.
Once two clients can interact smoothly through the server then it will be time for customizing player avatars and other more advanced features.
urls2linksSimple is a simple PHP function which parses text for URLs and converts them into links. I’ve plugged it into World Daily Press and The Free Speech Zone.
Previously you have to use HTML manually to create a link. Now you can just type in the URL and it’ll be converted into a clickable link automatically.
One of my least favorite features of MS SQL is the stored proc. Maybe it’s just the lack of a nice database management tool such as PHPMyAdmin that I have to work with.
However I discovered why they must exist: because open source developers are lazy and refuse to fix important bugs in any reasonable time frame.
Seriously, this problem has existed for at least 2 years. Apparently it’s too “niche” of a bug for anyone to take seriously and fix.
The problem is PHP’s ODBC driver. What is happening is simple; left joins are treated as though they are inner joins. And there’s no way around it. In ODBC land if you join table B onto table A and the join condition fails you don’t get a row even if you’re doing a left join and are supposed to. A left join is supposed to result in a row as long as the where clause is true regardless of the status of the join.
To get around this problem you have to put the query into a stored proc. The problem occurs when the ODBC driver tries to compile your query and pass it into MS SQL. So you just let MS SQL handle the query and have ODBC pass the EXEC call to the stored proc.
This problem is of course on top of the problem that the latest version of PHP comes with an ODBC driver that is completely incompatible with MSSQL 2005.
Sometimes open source is impressive. Most of the time there’s a whole lot of fail going on.
One of the hidden features of the Bunnies server was the ability to send and receive text messages. One of the hidden features of the Bunnies client was to receive text messages. So adding text chat to Bunnies was not that difficult. The main issue with the server was a gotcha in PHP. I’m sending up a byte that represents the message type. When switching based on that type you can’t use the numerical value of the byte. Say I send up byte 100 for a text message. I can’t check for the value 100 to process that text message. Instead you have to switch on the case chr(100). I’m guessing the issue is that PHP treats the case 100 like it’s the string “100″ and not the integer value.
Hooray for duck typing. Sometimes strongly typed languages are nice. Fortunatly languages like PHP provide ways to force things to operate the way you want.
On the client side there was already a switch case in place to handle text messages but it was commented out. In order to add messaging support I had to add in a message handler class which takes care of keeping track of all received messages and the current state of the user’s latest entry. I also had to add a new font for messages and allow the user to switch on text entry mode. Pressing the “~” (tilda) key will display the input area and the most recent received messages up to 10. It also switches the keyboard entry from moving around to text entry. Press it again to go back to game mode. The game doesn’t pause just because you’re in text entry mode.
One of the quirks I found with C# is that there are two keyboard handling events. key_up and key_down get you the integer value of the key you pressed (no real relation to ASCII) and key_press gets you the character. So if the user hits shift + “c” you get the char “C” which is really really nice. Bunnies now handles all three of those events since movement is based on the interger value and text input is based on the char value.
One of the features of Bunnies is that in order to send and receive messages other than your own you have to be logged in. That requires an account with the Bunnies web-site.
I’ll be doing some more testing to verify that all works and then hopefully I’ll have a new release of Bunnies out in less than a week. It may even include the ability to see other players who are in the same map as you.