The three hardest things in C are: (1) keeping track of memory allocation, and (2) off-by-one errors.

January 2018

Covenant MUD login screen Ah, the start of a new year. It's usually hard to get anything accomplished in December because the holidays are a bit of a time-sink, but I think I managed to make reasonable progress (see January's Release Notes). And considering that I initially planned on starting this project in the spring, I'm well ahead of the game at this point.

One thing about coming back to the MUD after fifteen years—or longer; I'm not really sure—is that I have forgotten which features are new, and which are standard. For example, I made myself a ticket to re-implement the window exit type. A window is a special type of exit that you can't walk through, but you can look through a window and see into another room. There are actually only two windows in all of Tierceron right now but this seems like a building feature we want to take advantage of. Turns out that windows are standard SMAUG and have been for a very long time, so the ticket became verifying the window exit type. (Yep, still works. Check.)

On the other hand, I had convinced myself that an object's "full description" was a standard feature, but that one was our own invention. It baffles me that stock SMAUG doesn't have a standard field to hold what you see when you look at an object; ordinarily, in order to get a message other than 'You see nothing special', you have to define an 'extra' description with the same keywords as the base object. Really, every object should reward the player with some kind of description, and having a full description field set on every object makes this an easy requirement to fulfill. If I type look shorts I should see something interesting, even if it's something silly like, "Do you like shorts? They're super comfortable and easy to wear!"

Interactive objects continued to be a dominant theme among the recent changes—one of the features that differentiated us from other SMAUG MUDs of the period was the detail we built into our objects. I have reimplemented the banks—the banking system itself isn't terribly exciting, but it does take advantage of our furniture object type: if you want to interact with a teller, you have to stand at a window to complete your transaction. I have also restored the 'shelf' object type. Shelves are special container-like items that you can put things on or in. If you look at a shelf, you can see its contents at a casual glance without having to examine it more closely. A shelf can be a regular shelf, a bookcase, or even a display case. We can create a display case that is closed and locked so that players can see the contents of the case but can't get the items out unless they have the right key—sort of like a transparent treasure chest.

As we approached the end of 2017, I wanted to find a project that would lay the groundwork for future development, so I chose to remove all of the built-in auto-mapper code from our codebase. The server-side mapper was ugly and generated cluttered-up room descriptions so I don't feel terribly badly about losing this feature. Players who want auto-mapping can always choose to connect with one of the well-supported MUD clients; client-side mappers do a better job than SmaugFUSS anyway. The real concern, though, is that the existing mapper code would cause conflicts with our RealSpace maps. If I could point to the one feature that really made us stand out, it was the RealSpace "overland" system that created a wilderness environment between zones. It's a huge project, but I should be able to start reimplementing pieces of RealSpace code this coming month. I am looking forward to writing more about RealSpace in my next update—it's a pretty cool feature and worthy of our time and attention.

Shamus

February 2018

RealSpace Map of Tierceron

The view from outside Tierceron's west gate

This is a long entry describing a relatively short list of release notes.

When a MUD world crosses a certain size threshhold, the implementors have to start thinking about how to connect their varied zones in a way that gives players a sense that their favourite locations are part of a larger world. Various types of worlds reward different play styles, and we wanted a world that would reward exploration and thwart the practice of "speedwalking". Speedwalkers are players who learn to get from one place to the next by programming strings of movement instructions into their clients. "How do you get to North Frobozz? Oh, from the town square you just go 6s6wn2wn3wn6wn2wnwsw3n3ws5w and there you are." These players never actually know (or care) where they are in the world; they just want to know how to get from one place to another. For all practical purposes, the intervening space does not matter. The rooms are all there to walk through, but the speedwalker does not stop to read them.

We put some thought into how to convey a proper sense of scale between the detailed zones and the world in which they are situated. The City of Tierceron is massive, weighing in at roughly 1000 rooms. How large would a wilderness area have to be in proportion? How many rooms would you expect to walk through to get to the next town down the road? If you write thousands of rooms as connective tissue, how do you make them all worth reading?

We weren't the only MUD looking for a solution to these issues of maps and scale. Among MUD developers of the 1990s and early 2000s, there were several attempts at fleshing out the wilderness space between points of interest. Some MUDs wrote large, mostly empty connective zones with traditional room descriptions. These connective rooms tended to get repetitive quickly, and invited the obvious question: why write all these rooms if they're not going to be very interesting? Other MUDs created ASCII maps that were essentially large grids of unremarkable rooms without descriptions whose sole purpose was to pad the space between more significant locations. To be fair, some developers—such as Richard 'KaVir' Woolcock—were doing some very creative experimentation with procedurally-generated maps and dynamic room descriptions, but by and large, the overland of most MUDs was implemented as a series of virtual rooms—a system that's still susceptible to speedwalking abuses.

Duithal proposed a system similar to the Ultima games of the 1980s, in which you would walk around a town until you leave via a gate, at which point the map changes scale and you are now in the world 'outside'. I don't think I need to describe this system in any greater detail because nearly every console RPG ever has employed something similar. Think of mainstream series such as Final Fantasy, Dragon Warrior, or Phantasy Star and you'll know how an overland map should work. It was a great proposal, but a Smaug MUD is not a console RPG. We had no idea how we could we could make this kind of map system blend harmoniously with the existing room-based model.

Around 2001, I was playing MUXes based on FASA Corporation's tactical wargame Battletech. These MUXes played out their battles on ASCII representations of the paper hex maps familiar to Battletech players. When you entered a 'Mech or a vehicle, you could navigate the map by setting your direction and speed and heading off, driving around from place to place in real time. You didn't move from one hex to another via exits; you just picked a direction and off you went. One particular MUX called this map system 'RealSpace' but I don't know if that term was universal. The BattleTech MUX implementation of RealSpace was pretty cool, but it was a dead, empty space. Aside from setting the occasional tree on fire, there was no way to interact with the environment directly. If you got out of your vehicle, you could walk around the map (very slowly) but you couldn't actually do much except get back in your vehicle and drive home.

I was intrigued by the possibilities of RealSpace. What if I could reverse-engineer some of its best features while making the environment interactive and giving it life? There are no rooms in RealSpace; there is just one big Cartesian plane of points specified with double-precision floats in C. You can be almost literally anywhere on the map. There are no virtual rooms; no exits from one map hex to another; just lots of space in which to move around freely along any arbitrary heading on the full 360 degrees of the compass. I wrote the first Smaug/RealSpace prototype during an 18-hour marathon session in 2002 (sleep? What's that?), and about a week later I had enough of the system up and running that a player could actually go "outside" and walk around a small map.

Once I had movement working, I brought Dracos into my test server, and loaded up a couple of horses so I could show him how to ride around the map. On a mad impulse, I tapped him, said, "You're it", and took off on my horse in a random direction. Dracos gave chase, and for the first time ever, a real-time game of tag was staged in a Smaug MUD. At that point we knew we were on to something special. It is this RealSpace system that I have begun to reintroduce to our MUD. There's still a fair bit of work to go to breathe life into our map hexes; I will discuss interactivity in a future update.

I've recently become aware of more MUDs that have well-thought-out wilderness systems. AFK MUD, a member of the Smaug family, has come up with its own implementation of an ASCII grid overland system, with an interesting view radius and a certain degree of interactivity with the environment. On the LP side, the creators of DartMUD, who've been around since 1991, employ a massive hex-based system with varied terrain where the hexes are like large rooms connected by exits. I'm happy to see these kinds of advancements continuing, and look forward to Covenant MUD once again taking its place among the innovators.

March 2018

Covenant MUD login screen

Don't panic, pullcheck—everything is under control.

J. C. Lawrence's "do it everywhere" law: "If you do it one place, you have to do it everywhere. Players like clever things and will search them out. Once they find a clever thing they will search for other similar or related clever things that seem to be implied by what they found and will get pissed off if they don't find them." Discussion Thread: An Essay on PK, MUD-Dev Archive. Retrieved 2018-03-01.

The main feature of this month's release notes is the basic implementation of RealSpace, which resulted in a couple of weeks of instability for the MUD. You can now leave the comfort of the city and stand 'outside', on the map, but doing so presents a problem. SMAUG, like all Diku MUDs, expects that everything that happens is going to happen in a room. If you drop an object, it lands in a room; if you look at the sky, you're looking at a feature of an outdoor room; if you scratch your nose, a message gets broadcast to everyone else who is standing in your current room. As far as the MUD knows, if you're not in a room, something has gone horribly wrong—the code will either throw an error message, or more often than not, simply throw up its hands in despair and crash because it doesn't know what to do.

At the start, building out RealSpace is an exercise in making "do it everywhere" a reality. If you can do something in a room, you ought to be able to do it in RealSpace. Out on the map, we treat hexes like rooms, so if you scratch your nose in RealSpace, that emote will broadcast to everyone else in your current hex. I've started with enabling communication commands, like socials, emotes, and global channels, along with some informational commands like equipment and inventory. The only commands you are allowed to use in RealSpace are those that I have worked on to make them function independently of rooms; for now, there is a limited set of commands available. My next step will be to build in some basic object interactivity like dropping objects, picking them up, and seeing the object inventory of a hex with look. After that, I will move on to allowing you to walk around on the map. Right now, you're pretty much stuck in the center of a hex standing in place. One thing at a time.

I also restored the Inn system, where instead of quitting the game anywhere you like, you find an inn and rent a room. We don't charge for rent at the moment, but if you want to keep the items in your inventory you will have to use the rent system. Inns function like save points in a console RPG, but they do require an innkeeper to be placed in a room, so you can't rent on the map. However, if you consider the principle of "do it everywhere", it's not hard to imagine some kind of camping skill that allows you to quit safely on the map, requiring some basic supplies, like a tent, and some supporting skills, like building fires. And if you can camp in a hex, maybe you should then be able to camp in certain wilderness rooms. Do it everywhere! Thinking about the relationship between hexes and rooms should provide inspiration for new ways to interact with the environment.

May 2018

In Programming Perl, Perl creator Larry Wall identifies the three great virtues of a programmer. The first of these virtues is laziness, and I've never bothered looking up the other two.

I took a bit of a break from the MUD so that I could devote some time to wrapping up some freelance gigs; between my side hustles and tax season, it was nice to have a bit of a rest. I've said before that I was actually planning on starting in the spring, and I've already addressed dozens of tickets. I'm feeling good about the current state of the MUD's development, even if I have been a bit lazy over the last six weeks or so.

I have been poking away at some code during this downtime because (of course) I can never just take an actual break. I spent some time refactoring the tell command, intending only to update the colours and formatting, but as soon as I got into the source code, I noticed something troubling. The tell command has a number of checks: a tell will fail if your intended recipient is invisible, in a 'silenced' area, not connected, or just flat-out ignoring you. These checks are all perfectly reasonable and expected. Smaug also has a reply command that will send a tell back to the last person you were talking to, and reply can fail for all the same reasons as tell.

Now for the troubling bit: every check in the tell command is replicated in reply. That's 160 lines of duplicated code! Both commands are responsible for evaluating the same set of checks, and transmitting messages to players when they succeed. This sort of duplication is the worst kind of laziness: all of the code in the do_tell() function was simply copied and pasted into do_reply(). No thought was given to the underlying structure or the relationship between these two commands. While this copy-paste approach is very easy to implement, it becomes difficult to keep the two functions in sync, because any future changes to one function have to be duplicated in the other.

The refactor that improves the code is an example of the good kind of laziness—a principle many programmers know as DRY (Don't Repeat Yourself) programming. If both functions have 160 lines of identical checks, then we should handle the shared logic in one place, and one place only. As it happens, reply is a special case of tell, in effect "send this tell to the last person who told me something." The new do_reply() function does some NULL checks and string handling (can't really get away from those in C), and then passes control over to do_tell() which takes care of all of the checks and sends the message. The reply function has gone from 160 lines to 12 lines which makes it much easier to read and understand.

After refactoring reply, I noticed that there's also a retell command, and without even looking at the code, it's easy to guess that this command duplicates all the checks (160 lines worth) I've already talked about above. This kind of copy-paste programming is all over the place in SMAUG, and I'm not really sure why. It's going to take some effort, but I think I'd like to DRY the code up. I'm too lazy to keep repeating myself, but sometimes being lazy is hard work.

June 2018

Still chugging along. I'm finding it interesting poring over code I wrote close to 20 years ago. If nothing else, such an exercise should show you how far you've progressed (assuming you have). Back then, I taught myself enough C to work on the MUD, but never really got into the subtleties of the language, so my code was somewhat clumsy. For instance, I was reworking some conditional blocks in Ruby, and I began to wonder, "Conditionals short-circuit in Ruby. Do conditionals also short-circuit in C?" It turns out they do. This language feature escaped my notice earlier; now, with 20 years' additional experience, I knew to look for it right away.

What is short-circuiting and why should we care? I offer the code fragments below as an example. The introduction of RealSpace forced the MUD to recognize that there are a lot of things that can only happen in conventional rooms, but not in map hexes, and as a result, I was writing a lot of blocks like the following:

  if (ch->in_room) {
    if (xIS_SET(ch->in_room->room_flags, ROOM_SILENCE))
    {
      send_to_char("You can't do that here.\r\n", ch);
      return;
    }
  }

In the block above, we first check if a character is in a room (instead of out on the map), and then we check if their room has the 'silence' flag set. If a character is NOT in a room, the outer block bails and the inner block never executes. I built this block this way because if the character is not in a room, the room flag check will cause a crash, so we need to make sure it doesn't get evaluated when ch->in_room == NULL. With short-circuiting, the block looks like this:

  if (ch->in_room && xIS_SET(ch->in_room->room_flags, ROOM_SILENCE))
  {
    send_to_char("You can't do that here.\r\n", ch);
    return;
  }

The condition is evaluated from left to right. If ch->in_room is false, then the whole expression is false—C never bothers with the room flag check and therefore never crashes trying to check up on NULL rooms. Using the short-circuit removes the nested if blocks and simplifies the code.

In addition to some code clean-up work, I've started fleshing out the functionality of RealSpace (see June's Release Notes). I'll write about some of that work in the next update.