I’m currently working on a major update to my Wildfire game multiplayer system, specifically fixing the mess I created four years ago when I wasn’t an advanced C# programmer.
The system needed to be updated anyway due to updates required for system libraries such as MySQL (the system currently uses Connector v5, when the latest is V8) but what I thought would be a small amount of refactoring has resulted in a complete rewrite of the entire system.
Within its SCM repository I called the branch “wildfire-1.6” but I’m considering upping it to 2.0… time will tell.
The current version is based on the Project Reality Battlefield 2 master-server, with features added such as using MySQL and support for Crysis Wars-specific functions. It crashes every couple of days like clockwork, and uses obsolete geo location data & other obsolete code.
In summary: it sorely needs an update.
Perhaps I’ve gone over the top with the new update, but I’m hoping to expand the Wildfire system to expand support for the Wildfire protocol to the point where others can use it for their own games.
The new update includes:
- No more static everything, the current version uses
staticvariables and classes all over the place. It really isn’t needed and using them without a good reason is bad practice…
- Threaded database calls- previously if two users connected at the same time one would need to wait until the other had connected due to the way MySQL handles data readers (one per connection). Now a queueing system is used with
Actioncallbacks, more on this below.
- Users are now actually recognised as “offline”- currently the system can’t tell when users go offline, but now it automatically updates their status to
offlineif a heartbeat hasn’t been received for three minutes.
- Better geolocation handling- Wildfire stores certain geolocation data for security and stats purposes, as above it currently uses an obsolete local location database. It now fetches the latest geolocation data for them via an API (GeoPlugin).
- Native event timers- need some stats reset at midnight? Now Wildfire natively supports that! The same system will be used to trigger user heartbeats and other actions.
- User data caching- an in-memory cache is kept, so that the database doesn’t need to be contacted if the user logs in again within a few hours. Using the event timer system above, the system regularly checks for changes to their status (e.g. password changed, was banned/unbanned, etc.) and acts appropriately.
- GameSpy support (Crysis Wars, Crysis, etc.) is now treated as “Wildfire Legacy”- they now reside in the
Wildfire.ServerModules.Legacynamespace and is part of preparation for the system to expand.
Threaded Database Calls
As mentioned above, the database system has been reworked significantly. The problem I found when I reprogrammed the base system is that MySQL only supports one
DataReader open at any given time, apparently this is a Microsoft restriction with C# (so not entirely the fault of MySQL Connector), so I needed to create a system to queue the requests.
That meant a significant rewrite of the database portion of the system because currently it is blocking, for example we could have
IsBanned would query the database there and then. This would be incompatible with a queuing system because we wouldn’t be able to give a response immediately: instead a system of callbacks must be used.
The system that I went with was that a request ID is generated that is sent to the database queuing system with the request, which is then picked up by one of the worker threads and executed. When the query or statement has executed, the system then calls the callback that is responsible for handling the database’s response. This callback then calls the callback the original method provided so the correct action within the system can be taken.
The number of worker threads is equal to the number of available logical processors (aka threads) available to the program, this means that if there are eight logical processors there can be a maximum of eight database queries/statements executed on each tick of the system (which is currently set to 30Hz, so 8*30 per second).
When a worker thread is being used, it is locked from being used by another query/statement.
Obviously in reality there might be a few database updates taking place on a single CPU core, and a couple on another. The operating system determines what threads are executed on different cores, but the new database system will still significantly increase performance.
In addition to the above, the system now constructs database queries and statements itself, to decrease the likelihood of me getting a query or statement wrong.