Archive for category Challenges
I don’t know if that’s really what I’m supposed to call this, but I know it means arranging the stuff in your room in a mystically perfect way, or something like that. And now we’ve built rooms, but they’re really basic, so we need to spiff them up. So that’s my desperate attempt to justify the title. Moving on…
The challenges in this post are as follows! First, those that are direct consequences of having just created rooms:
- Now that you have rooms, so that people can be in different places, you need a shout command. Because not being able to be annoying will just never do. It’s easy: Just copy the say command and change
- Add short descriptions to rooms. These will have many uses; I list a few here, the first two of which you can implement now if you like it, and the last of which we’re not yet ready for.
- Add to the who list information about where the player is.
- Have the look command print the short description as a title above the long description.
- When a player enters a room, tell them where they’ve arrived in one sentence (using the short desc).
- Expand the look command to list the objects in the room, after the room’s long description.
- Remove unneeded code from say.c and other files like it, where the
__NO_ENVIRONMENT__define is queried. Since we clearly both have and use environments now, that check is unneeded.
And one final challenge that’s completely miscellaneous:
- Improve the update command, which is not very helpful with its spammy errors.
- If the file does not exist, say so and exit without trying to load it.
- Wrap a
catch()call around the
load_object()call, and print the error message returned, if there is one. This makes the loading error clear, rather than reporting a pile of errors and requiring you to sift through them to find the one that was actually about the code in the file being updated.
Here are a few random items I did at this point in my development that you may want to do also. They’re minor, so I’m not showing you how to do them as a tutorial, but just issuing them as challenges.
- Wherever you have full path names as string literals, begin to replace them with #defines in globals.h.
// old way: user = new("/clone/user"); // new way: user = new( USER_OB ); // assuming USER_OB was #defined to be "/clone/user" in globals.h, // so you can change it later if you need to in just one place
- Tweak eval in several ways:
#include <globals.h>, so that if you need to use things like USER_OB and its friends, they’re available.
- I don’t know about you, but I find it disconcerting when
eval 2+2gives me zero. This is, of course, because the code eval writes doesn’t have a return statement, so the default return value is zero. I therefore tweaked eval to insert a return statement so I didn’t have to type
eval return 2+2(or whatever) every time.
- However, this means your evals have to be single statements, and maybe you don’t want that. So I also tweaked eval to allow code before the return as well.
eval 2+2results in evaluating a function containing
return 2+2;, while
eval |int x=2;| x+xresults in evaluating a function containing
int x=2; return x+x;.
Sure, it’s a little convoluted, but the first use case is way more common, and I forget the “return” all the time. Pick your poison.
- Run the result through the
dump_variable()sefun for prettier output formatting.
- Create an alias command. If you’re not familiar with aliases in a MUD, they’re customizable typing shortcuts. For example, let’s say you type “look” a lot and you want to just be able to type “l” instead. You issue this command:
alias l look
And then “l” is a shortcut for “look” thereafter. Implementing this requires several steps:
- Creating a place in the user object to store aliases. Rather than creating this as a one-off, I created a generic property dictionary in which any future permanent values like this could be stored.
- Creating a command (in the commands folder available to everyone) that allows the user to manage their aliases (add, remove, see the list of currently defined ones).
commandHook()in clone/user.c to run every line of user input through the alias expander before processing it.
Then you can do cool stuff like this:
alias me eval this_user()-> me query_name()
More challenges for you!
- Create an ls command that works like the regular Unix one. Append / to the end of the names of folders, so it’s evident which things in the list are folders. Note that the get_dir() efun, if passed the optional second parameter, will give you information on what’s a file and what’s a directory.
- Create a cat command. Turns out there’s a handy function for this, too. Check simul_efun.c for cat().
- Make all filesystem commands (including those you didn’t just create, like update, rm, and ed) interpret directories using the resolve_path() simul_efun. But don’t forget the upcoming gotcha that fixes that function to be not annoyingly wrong.
This is the second “challenge” post. The first challenge post explains what they are.
Improving the “say” command
The first half of this challenge is to improve the “say” command. As you recall from previous posts, commands are in /command/*.c. Here are some things wrong with it that you should fix.
- It does not print any message to the person doing the speaking. That can be disconcerting, and is confusing when you look back over a conversation to see only the other person’s comments. (In fact, it doesn’t look like a conversation at all!)
- It does not capitalize the names of the people speaking. For example, you might see this:
jim says: hello
That’s hardly good style. Tweak this so you get this instead:
Jim says: hello
- If you’re feeling really ambitious (I wasn’t) you can make it use quotation marks correctly, like this:
Jim says, “Hello.”
Recall that this can get quite confusing with nested quotes and whether the user put their own punctuation on, etc.
In an earlier post I advocated replacing all publicly-facing instances of the string “Lil” in your lib with your MUD’s name. There are two problems with this.
- Although I happen to be writing a lib-and-MUD combination, you may just be writing a lib and intending others to make MUDs. So why hardcode the name of the MUD into your lib?
- It forces you to put the same string in more than one place (not only the one or more instances of the thing in the lib, but also the instance you already put in the config file way back when).
It turns out that the name you put in the config file is actually available to you, in code, provided by the driver. The MUD_NAME #define is guaranteed to be a string containing the name from the config file. (Who knew? Well, the docs did, actually. That’s where I found it.)
So the second half of this challenge is to use MUD_NAME instead of each instance of the actual string literal containing the name of your MUD. Just a search-and-destroy, this one.
This is the first “challenge” post. It will give you tasks that you can do if you feel like it, exercises for the reader, you know…homework. This is based on the login functionality I gave code for in this post.
Perhaps these things won’t matter to your MUD, but I found myself doing them, but not finding them interesting enough to post about. So instead I’ll just tell you to do them if you like. (This way I’m also not leaving out any important corner cases from consideration.)
- If a player presses enter without typing a name at the name prompt, they probably want to quit. Disconnect them immediately.
- Whatever name a player types, treat it as the lower_case() version of itself.
- Do not accept blank (or very short) passwords.
- Stop appending an OID to the end of the player’s name. And, as a necessary consequence of that:
- If a player with a certain name is already logged on, and another logs in under the same name, disconnect the first player and let the second player take over the user object of the first player. Check out the useful efuns find_player() and remove_interactive().
- If two players try to create a character of the same name at about the same time, what will happen? Is that acceptable to you? If not, how could you fix it?