Archive for the 'Data Saving and Loading' Category

A Whole New Spell And Skill Engine

Saturday, April 12th, 2008

Well, maybe not a *whole* engine quite yet, but certainly two thirds of one.

I’ve generated about 500 little XML files to hold all of the skill and spell data for dynamic runtime loading.  It’s all pretty neat — skills and spells can be tweaked by hand without having to compile any code (though the MUD requires a reboot) and they’re loaded at runtime, stored in a Dictionary type, and accessed based on their names.

There isn’t a single hard-coded spell or skill value in the engine.  Hardcoded values were something that always bothered me.

Another thing I did in this process is embed “logical preference” data in the skill and spell files so that the AI engine can be improved while at the same time removing the need for thousands of lines of “spaghetti code” like in Basternae 2.  You see, each spell and skill check in B2 was hardcoded in a specific order and with a specific percentage chance.  Adding a skill or rearranging mob AI meant editing these in more than one place.

Instead (when the AI code is done), we’ll be able to set a few flags on each spell or skill and it will handle it automatically (unless a specific mob has a personality override file).

Oversimplified example:

Fireball.xml:  Type = offensive, Preference = 65, Likelihood = 40
LightningBolt.xml:  Type = offensive, Preference = 50, Likelihood = 50

This means, essentially, that a mob would have a 40% chance of casting fireball during combat, and if that didn’t go off, it would have a 50% chance of casting lightning bolt.  Changing the way the mob performs in combat is just a simple number tweak.

I have some pretty huge plans for mob AI, but this new spell/skill system goes a long way toward making those plans easy to implement.

I also have yet to embed all necessary custom code in the spell/skill files, but one thing that’ll be done is that most information needed to trigger a spell will be embedded in the file.  Instead of manually writing a dozen lines of the same code for each spell to validate the target, check saving throws, set damage type and amount, send messages, and deliver the affect(s), most spells will use one general-purpose function that checks the spell types and flags and executes the spell’s action.

This means that most standard spells that are just damage or single-affect, like “soulshield” or “fireball” won’t have any embedded code at all.  Instead, only super-involved custom spells like the enchantment spell “earthen smith” will have their own embedded instructions.

Well, that was long-winded.  It had to be — I’ve done a lot and I’m pretty proud of what I’ve accomplished with this.

Learned Something New Today

Friday, April 11th, 2008

I came across this article on Scripting with C#.

Quick summary: It tells how to load and compile C# code from within a running application so that you can dynamically load scripts.

This is something I’ve been meaning to find out how to do for a while. I’ve always wanted to create a file-based spell system that loads and compiles all spells at boot time so that they aren’t so tightly integrated into the engine. As it was historically, if you removed a single hardcoded spell, such as “armor”, the entire mud would crash or at least be very unhappy.

Well, what good are 400+ hardcoded spells going to do you when you use the mud engine for a sci-fi, historical fiction, or contemporary post-apocalyptic setting? None at all. That’s why getting them once-removed from the core is something I wanted to do. Super-long-term there’s not just one MUD coming out of all this effort.

The Joy of the Flags Enumeration

Tuesday, November 27th, 2007

In the old days of MUDs it became a rather common occurrence to use each bit of a 32-bit integer as a binary flag to set or unset a value. This was far more efficient than using an array of 32 boolean values because the integer consumed 4 bytes, while an array of 32 booleans consumed anywhere from 32 to 128 bytes, depending on the system architecture.

This helped to save a great deal of storage space and memory, which was incredibly important given the machines of 1991 or so: a 33MHz 80486 processor with 16 megabytes of RAM was pretty typical.

Even with systems a lot cheaper and a lot faster now, the amount of RAM and processor time you use is still a pretty big determinant of the type of hosting plan you will need to purchase. Saving RAM is still a good idea.

Take, for instance, the “system flags”. It’s a group of flags that control certain things about the game, such as whether equipment wears out in combat and whether mobs are limited in the number of spells they can cast in battle. Stored in the new XML format as an integer, a typical value is:

<_actFlags>8256</_actFlags>

That’s not very descriptive.

We can make it a little more descriptive by using an enum tagged with the FlagsAttribute. Here’s how we implement it:

[Flags]
public enum MudFlags
{
none = 0,
turbolevel = 1,
equipmentdamage = 2,
mobcastslots = 4,
mobslootplayers = 8,
autoprice = 16,
walkableocean = 32,
nameapproval = 64
}

In our system data class we have a variable that is a MudFlags type.

As with an enumerated value, this type uses the flag names for serialization and deserialization. Now we can see what things are turned on with a quick glance at the system data file:

<_actFlags>equipmentdamage autoprice</_actFlags>

So, equipment damage and automatic pricing is turned on.

This is also handy for displaying what flags are set — we can just use the ToString() method of the MudFlags type to get more info.

.Net XML Serialization

Sunday, November 11th, 2007

Most of the saving and loading of MUD data in Basterne 2 was handled by low-level C functions that used a custom text file format for each data type. That is just a bad idea.

Save and load functions were typically dozens or hundreds of lines of code with lots of room for error and data loss. If a word was off by one space or a character was miscapitalized, a file load would fail and nothing could be done. Players of Basternae 2 probably remember pfile corruption issues with anything but fondness.

Every time we changed a file format by adding another value to an object, we had to build a translator that would convert the old file to the new version. Usually this wasn’t too hard, but it did result in a LOT of extra code lying around where it didn’t belong.

.Net makes this a lot better by giving us XML serialization.

With XML serialization, data is dumped out in a neat little XML file that can be read not only by our MUD server, but by just about anything that can read XML, which these days is just about every application in existence. Gone are the days of low-level custom file formats and painful data corruption issues (I hope).

So, how do we do it?

For example, I just rewrote the saving and loading of fraglists to use XML serialization. The code started as 174 lines of custom textfile saving and loading with lots of recursive looping to save and read the arrays containing numbers on the frag list by race and class.

Now the code looks like:

public void Load()
{
XmlSerializer serializer = new XmlSerializer( this.GetType() );
Stream stream = new FileStream( Database.SYSTEM_DIR + Database.FRAG_FILE, FileMode.Open,
FileAccess.Read, FileShare.None );
fraglist = (frag_data)serializer.Deserialize(stream);
stream.Close();
}

public void Save()
{
XmlSerializer serializer = new XmlSerializer( this.GetType() );
Stream stream = new FileStream( Database.SYSTEM_DIR + Database.FRAG_FILE, FileMode.Create,
FileAccess.Write, FileShare.None );
serializer.Serialize( stream, this );
stream.Close();
}

Cleaner and better, just the way we like it. I still hate the way Wordpress formats source code.

The compile error count is now down to 1,649.