Monthly Archives: April 2008

Removing Items From A Container In A Foreach Loop

It wouldn’t have been unrealistic for the designers of .NET to find a way to make this work:

foreach( Item i in ItemList )
{
  if( i.ShouldBeRemoved )
  {
    ItemList.Remove(i);
  }
}

What happens is you get a ‘collection modified’ exception and you’re hosed. You can’t move to the next item in the list because removing the item broke the list. It wouldn’t have been that hard for the design of IEnumerable (the thingy that makes foreach possible) to keep track of where the next item was even after a removal.

Instead of being able to use the above code, something like this clunky bit is required:

for( int i = (ItemList.Count – 1); i >= 0; i– )
{
  if( i.ShouldBeRemoved )
  {
    ItemList.Remove(i);
  }
}

It’s like telling someone they can avoid head-on car collisions by always driving in reverse.

Anyhow, there was a bit of a problem with the Select() command and socket management, and this was the solution. The socket code is now functional and stable enough that I can log in and run around trying to play the game. It’s not terribly playable yet — I have a *LOT* more work to do, but it’s still theoretically possible that a development server could go up i mid-but-more-likely-late May.

Sorting The Easy Way in C#

If you have a list of objects, say List<Widget> it’s mighty easy to to sort them using .NET.

First, derive the Widget class from IComparable:

public class Widget : IComparable

Then create the CompareTo method. In this case, we’re sorting the list of Widgets by name:

public int CompareTo(object obj)
{
if( !(obj is SynthProfile ))
{
return 0; // We could throw an exception here if we wanted to.
}
return this.Name.CompareTo(((SynthProfile)obj).Name);
}

Once that’s done all you have to do is call:

WidgetList.Sort();

Easy.

Raph Koster Is Pretty Awesome

Some of you may remember Raph from Ultima Online, Star Wars Galaxies, or Everquest II. He also wrote a few articles for Imaginary Realities webzine (a webzine about MUDs for those of you who don’t know). The site was pretty active from 1998-2001, but at some point it vanished from the face of the Earth.

Well, Raph was nice enough to give permission to reprint his IR articles on FindMUD. They’re a good read, and still relevant today, so if you’re at all interested in MUD design you should read ’em over.

Raph’s blog is also pretty interesting and updated fairly regularly. It’s worth becoming a regular reader.

300 MUDs on FindMUD

Data entry is a lot of work and it takes a lot of time. Even so, I’ve been making steady progress on FindMUD. It’s up to 300 MUD listings now.

Our next goal is to pass MudMagic, which has just over 500 listings. It won’t be blazingly fast, but it will happen.

A New Command Processing Engine

Command processing in the old Basternae was pretty klunky. The command interpreter would just pass on any text that was entered and each command function would have to do a lot of parsing to split up the command strings and figure out what the user actually intended. About half of each command would be devoted just to breaking up strings in some cases.

Luckily with .Net we have all these nice string functions — Split, Join, Substring, IndexOf, Remove, and many more.

With the new command processing engine I’ve just written the command functions are a lot easier to read, maintain, and create more of.

Of course, I’ve had to redo every command in the process and that’s only 80% done so far, but it’s a far better system overall.

And, of course, I still have to finish the replacement spell system.

Automatic Line Wrap

When writing a zone, it can be tough to know where to end your lines of text.

While a standard terminal has 80 characters, some telnet programs start to look weird with any line that is more than 77 characters, and some terminals have 130 or more characters per line (usually depends on screen resolution).

With different zone writers writing descriptions with varying line lengths it can make the MUD look pretty inconsistent from zone to zone.

That’s why I wrote an auto-wrapping function that takes care of all of that.  It will take a description and insert line breaks as necessary.  Right now it just defaults to a 78-character terminal width, but it will be user-configurable when I’m done with it.

This means that zone writers don’t have to worry about line breaks anymore.  They can just type out their descriptions and it’ll be handled by the MUD.

Word wrapping still isn’t perfect, so I’ll need to work on that a little, but it’s pretty neat to have autowrapping in place now.

A Whole New Spell And Skill Engine

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

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.

Writing A MUD Engine Is Hard

Seriously.

I don’t mean just writing a basic telnet chat server with a few objects and commands you can interact with.  Any amateur programmer can do that.

No, I mean writing a full-featured MUD engine that supports all of the features MUDders have come to expect from a game like Basternae 3.  It’s really dang hard.

I’ve spent hundreds of hours working on this rewrite and it’s still pretty far from done.  The area format is still changing quickly enough that I don’t have a working zone editor I can pass out, and I still have a whole spawn engine to write (for those of you who know MUD internals, it’s more-or-less the Basternae equivalent of “resets”.)  I also have to write some sort of mob-action-scripting engine.

It’ll be worth it when I’m done because I’ll have an engine that has been completely written by me and nobody will be able to say what I can and can’t do with it, but it sure is arduous — it takes a damn long time to write 100,000+ lines of code (as you can see by noting that the B3 blog posts beginning about a year ago).  And I’m no newbie coder — I’ve been writing code for more than 20 years, MUDs for 6-8 years depending on how you count the gaps, and commercial code for 3 years now.

That’s why Basternae 3 isn’t open yet.  I could easily have taken the old zones and code up and just went with that, but that’s not something that supports my long-term plan.  Maybe I’ll mention more about that plan at some point.

Humpy Dumpty It Ain’t

A little less than a week ago I mentioned that I had broken the heck out of the codebase.

Well, it’s all back together again.  The MUD engine and the zone converter are as healthy or healthier than they were before, the weather’s nice, and all is well with the world.

MUD Connection Stats on FindMUD

One thing I’ve been working on is an automatic ‘ping’ routine that checks the MUDs listed on FindMUD every few days and shows the connection results on a MUD listing so you know whether a particular MUD is still alive or not.

Since I’m no PHP expert, it tends to be rough going sometimes. I know just enough PHP to be dangerous — it’s a lot like C but not *enough* like C for me to be comfortable yet.

Even so, I’ve managed to put together a semi-automatic routine that checks these connections. Here’s how it works:

  1. I go to the site and export the master MUD list. This is a one-click PHP script that gives me a CSV file.
  2. Paste the CSV file into a desktop application I wrote in C# .NET that cycles through the list and tries a connection to each MUD. It generates two SQL queries for me — One that updates the IP addresses for any MUDs whose servers have moved and one that updates the connection results table in my database.
  3. Take those those two queries and execute them on my database server.

It’s a seven minute process and not too much work, but it would be nice if it were fully automatic and happened at a specified interval instead of “whenever I do it”.

The two roadblocks I have at the moment are that the shared hosting I’m using for FindMUD doesn’t seem to allow outgoing telnet connections and they also don’t allow connections to a database from outside the server. Even though I could whitelist my IP address(es) in order to connect to the DB I don’t really want to open up any of my databases.

Being able to do #1 would automatically make #2 a non-issue since connections would be made from the server running the ping test. When I get hosting for Basternae 3 it’ll be on a server I can do whatever I want on, so this process should be able to become fully automatic.

Fedora Core 8 Just Doesn’t Work

I’ve never had any luck with Fedora Core.  The latest version, 8, wouldn’t even get past the disk partitioning screen for me.  There’s a reason why I’m an Ubuntu devotee — after all, it managed to install itself quickly and easily with no stupid questions and worked flawlessly from the start.

Last time I ran Fedora it was on a set of 4 Dell desktops.  I started with 4 and later tried 5.  There was something wrong with the SATA driver and the hard drives would forget their data after about two weeks on average.  After reinstalling two or three times on each machine I ended up switching to Ubuntu.  No problems at all after that, although I did really want a “Fedora Core killed my family” shirt.  Too bad they don’t exist.

So, it looks like the Linux port of client/server development will still be on Ubuntu.

If you can’t run reliably on Dell hardware it’s time for you to go home.

Mass Refactoring

In the interest of making sure the MUD can be modified easily well into the future I’ve broken the heck out of the codebase.

Yep.

I’ve been refactoring, rearranging, renaming, and restructuring things so they make a lot more sense. That has inevitably broken a few things, but it will be far easier to add new types, flags, and enumerations in the future — things like adding a new terrain type without breaking existing terrain types or having to recompile the editor and re-convert all of the zones.

Believe me, we had quite a headache with zone file format changes on Basternae 2 and I don’t want to repeat it.

This means that it’ll take a few days before the codebase will build again and I’ll have some work to do on the converter and the editor, but it’ll be worth it.

I’m still looking at putting up a test server in May and so far Slicehost is the most likely candidate. Feel free to offer suggestions. Since it’s out-of-pocket and non-income-producing, my budget isn’t any more than $20 per month (should I be accepting donations for this?)