The AIME Codebase Code Description An Overview: The AIME code (stands for Advanced Interactive Mudding Environment) was started over a year ago because of the perceived inadequacies of the Aber code. In designing the code, attention was placed on flexibility and efficiency. The idea was to provide the mud creator an easy way to create almost anything he or she could think of without requiring them to touch the code very much. It uses an object-oriented design to facilitate flexibility and variety in object/location/mobile types without requiring large memory signatures per object. Mud games provide an excellent environment for Object-Oriented design since everything is essentially an object in the game of some sort or another. The AIME code has been designed to be as flexible and as efficient as possible. Effort has been placed into ensuring this, but you may find something that you think could be done better a different way and if so, please let Slate (noelg@acm.org) know. Before reading this or starting to code on the mud, you should have a fairly good understanding of both C++ and object-oriented design, as the mud has been designed using many of those design methods. The Configs Object: Configuration information for the AIME codebase is loaded into the_config object (Configs class, defined in configs.h) from the first file found of "../data/aime.conf", "./aime.conf", "./data/aime.conf", "/etc/aime.conf", or "/etc/aime/aime.conf". Or if not found, the user is prompted for its location. The Main Mud Object: The entire mud is run by a single mud object (Mud class, defined in mud.h), which maintains everything from the player list and object database to the listening socket. The only globally defined variable should be mainstruct, which is a pointer to the Mud object. When the mudbin executable is run, it first creates the Mud object, daemonizes the mud (if "Daemonize" is set to yes in the aime.conf file) and then starts the main mud loop. When the Mud object is created, the constructor function (Mud() in mud.cc) is run and initializes all objects needed for the functioning of the mud. First, logs are started, then the mud is "bootstrapped", meaning it loads all environment variables, areas, actions, and levels into memory. It then starts the user database manager, and finally the game and builder listening ports are initialized and started. Once the mud is loaded, it then starts up all game timers (objects that keep track of things like game time, weather changes, time of day changes, checks mobiles for action, etc) and enters into the main loop (located in main.cc). First, it checks to see if the mud is shutting down and if not, checks for quitting players. It then checks all players for activity, all builders for activity, and finally checks the mud for events. An event could be something like mobiles moving, attacking players, or a torch burning out. This main loop is where all mud activity takes place. If input is found while checking the players, it handles it...possibly running commands that manipulate things on the mud. The commands can access mud structures in the main Mud object, such as the object database, the user database, or even send messages to other players. The Mud object acts as a central repository, bringing together all aspects of the mud. The following objects are contained in the Mud object and described in detail. The Port Object: The Port object is the listening port that accepts connections. When a player connects to the mud, the listening socket will detect their connection, accept or deny it (based on an access control list located in the Mud object as GameAccess and BldrAccess object), and create the Player or Builder (depending on the port). It creates a Connection object (described later on) that handles all user activity, such as receiving input or sending room descriptions to the player. You can set how often it checks the listening port in the aime.conf file, with the "PollsPerSec" setting. The Access_List Object: The Access_List object called builder_access_list for the builder port or game_access_list for the game port, maintains the list of hosts that can access the specified port. It permits for defining a security default (allow by default, deny by default) and then a list of hosts which are exceptions to the default. If the access list denies a host, it is allowed to connect only long enough to inform the connection that it has been refused access. The Object_List Object: The Object_List object called mudobjects maintains the entire object database that stores all the areas in the mud. It consists of a binary tree (uses the class btree_a to maintain this list) of Area_Dbase objects (discussed next) which in turn store all objects in the game, from weapons to spells to quests. The Object_List object provides methods for adding, removing, and retrieving objects in the database. It also provides the cloning method which allows you to safely duplicate objects in the mud. It also manages resetting of zones, ensuring safe reloads without game objects getting messed up. The Area_Dbase Object: The Area_Dbase object is what stores most of the game 'objects', ranging from mobiles to locations. It also stores objects on the builder port when a builder loads an area. Even help files are stored in an Area_Dbase when the builder loads the help area. They can then modify help files and 'commit' them, which writes all help files back with the new changes. Area_Dbase objects provide the means to gracefully reload zones on a lower level than the Object_List object. Spells and Skills (Abilities) are stored in an Area_Dbase object during gameplay and retrieved when a player casts or performs them. The objects in the Area_Dbase object are stored in a binary tree for fast lookup (the btree_l object handles this binary tree) (with a linked list also present to facilitate searching through all objects) and are stored as Entity objects (discussed later). Verb_List Object: The Verb_List object exists in two places in the Mud object. The first handles all commands available for the game port and the second all commands available to the builder port. It stores both verbs and actions in a modified lexical tree. (The lextree object handles all methods and attributes for this) The tree is modified in that there is only one node per word, as opposed to the normal way of doing a lexical tree which would provide one node per letter of that word. The modified lextree is much more efficient memory-wise and has a faster lookup than conventional lextrees. However, it does have a much less efficient add and delete function. Since we rarely add or delete after the first bootstrap, this is not really a problem. A Verb object stores the verbs in the lextree and an Action object stores the actions. The Verb object stores permissions for that verb (see comflags.h for command flag permissions) and a pointer to the function that should be run. An Action object stores the strings that a player sees when they perform an action. Player Object (subclass of Individual, discussed later): The player object, attached to the Mud object by a linked list, is what stores all player attributes and handles much of the player interaction with the mudworld. It provides functions to interact with the Connection object, which handles the player's network connection (discussed more later on). It manages name, prompt, description, abilities (spells and skills), getting objects into the inventory, dropping objects, and storing and reading data to and from the player's data file. It provides means for sending text to the player's screen and allowing the player to see their completed quests. Builder Object (subclass of MudObject, discussed later): The builder object, attached to the Mud object by a linked list, is what handles the builder's interaction with the building port. It handles loading zones (stored in the Builder object with an Area_Dbase object), modifying of objects in that zone, and interaction with the builder's network connection, attached to the builder with a Connection object. Connection Object: When a new connection is received at the Port object, it creates a Connection object. On creation, the Connection object will accept the connection, store the IP and host (if it can get the host), and initialize the socket for non- blocking (meaning if it does not receive data it won't keep trying to find data until it does and hang the mud). The Connection object handles attributes of the connection, such as should it use color, have we lost connection and if so, how long ago (for linkdead purposes). It maintains the read and write buffer for more efficient socket functioning. It also modifies incoming data to a form the mud can read, as well as modifying outgoing data to a form the player's client can read. This involves such things as converting the colorcodes to ANSI color codes and adding \r to lines if necessary. User_Dbase Object: The User_Dbase object maintains the player database. When a player tries to logon, the Port object will create the Player object (and subsequently, the Connection object will be created) and a login input handler will be pushed onto the player's Inp_Handler stack (discussed next). The login functions will ask the player for their name and when received, it will turn to the User_Dbase object to request data on a user by that name. It will fill the Player (or Builder) object with that data, including the password, and then ask the player for the password, comparing with what it loaded from the User_Dbase object. The player data is stored in a directory (one for each letter of the alphabet) as .user under the ~/aime/data/users directory. Inp_Handler Object: The Inp_Handler object is attached to a Player object, and controls which input handler function should be used to gather input from the player. It consists of a stack of handlers, which store information on that handler, such as the prompt to use, the command to run, what function to run (if any) when the input handler is popped. It also can store data, such as a Pager object (discussed next) and Mailer object to interact with. The main input handler should always be there and it gets the player's input and runs the appropriate command based on that input (all commands are located in commands.cc, bcommands.cc, and jcommands.cc). When a player enters the mailer, the mailer input handler is pushed on the stack and will interpret all commands until it is popped, where it will refer back to the last input handler. Pager Object: The Pager object provides a means to control output to the player. When created and a string or file is passed to it, the pager will push a pager input handler on the stack and it will display x number of lines, prompting the user to press enter to continue. Once all lines have been displayed, it will pop the pager input handler and return to the previous input handler. Level_List Object: The Level_List object controls the list of levels for gameplay. Levels are stored in Level_Chain objects, which store the Level objects (discussed next) in a sorted linked list from lowest level to highest. The Level_Chain objects are stored in the Level_List object in a linked list, which basically just manages the list and provides methods for searching out levels, adding levels, and checking to see if a player has advanced a level. Level Object: The Level object maintains all data on a certain level, from what criteria a player must meet to advance to this level to the string to display when they do advance. When a player has their level checked via the Level_List object, it checks their current level in the chain, checks the criteria in the next level to see if the player meets it, and if needed, advances the player level. On level advance, the award_string string is displayed to the player telling them that they have advanced a level. Logs Object: The Logs object maintains the various logs in the game. It provides methods for opening the logs, archiving the logs, displaying the logs, and finally closing the logs. Various subclasses define particular attributes for each log. In addition to the inherited Logs methods, the ErrLog subclass provides a method to write errors to the logfile. The SysLog subclass is similar, providing a tailored method to write system log entries. Finally, the Data_Log subclass provides methods to allow the players to submit information to the mud administration. This is used in the suggestions log, the bug log, and the typo log. Timing Object: The Timing Object maintains the game clock and keeps track of how long it has been since we performed a particular action. The game may up a player's health once every 3 seconds, and this object keeps track of how many seconds it has been since the last health increase (gradual healing). It also keeps track of the time of day (is it night, morning, etc) and the time since last level check (checking if players will increase in level). Thats all the objects that are in the Mud Object. The rest of the Mud object attributes perform various functions to maintain the game and should each be documented somewhere in the mud code. There are a few objects that have not yet been discussed, (with the exception of Entity and all subclasses, discussed last) and I will go through them here. Lexer functions: The functions in lexer.cc provide an effective and safe means for reading in a file. It divides out words and numbers, placing them in a token and marking that token with the type of data stored there. It ensures that in the process of reading a file, you don't crash the mud. If you are going to read in a file, you should use this to ensure you don't introduce errors into the code. LinkedList Object: The linkedlist object maintains a linked list that is used in storing the inventory of any game object. It stores player's inventory or the contents of a sack. The path of User Input: With the normal game or builder input handler, it uses a few objects before it finally executes the command. First, the Connection object detects the input and reads it in, passing it off to the player's input handler. The input handler then creates an Input object which takes the input and determines the appropriate verb needed. It then passes the input string to a Parse object, which breaks it up into sections depending on the verb format (see verbs.h for a list of verbs and their assigned formats). Finally, the Input object calls the function, passing in the Parse object and a pointer to the player or builder. Editor object: The Editor Object is used to handle editor sessions. This provides an easy way for the user to input multi-line text. In full mode, the editor provides functions for inserting, deleting lines, replacing words, and a few other features. Flags Object: The Flags Object provides for a compact way to represent many flags in one or more long integers. On creation, you give it how many sets of 32 flags you want (how many long ints it should use) and it will then maintain those ints for you, using methods to give the impression of one large set of flags. Mailer object: The Mailer object handles the player's mailer. It maintains attributes describing the mailer, such as the filename to use and the username who owns the mailer. It reads in mail from the file, adds new mail, removes old mail, and displays mail to the screen. It holds information such as if the player has new mail and maintains a linked list of Letter objects. The letter contain all the information for each piece of mail, such as the title string, body string, and if the mail has been read yet. The Letter object provides methods for delivering mail to others as well. The file mailer.cc also contains the input handler functions for proper mailer functioning. Strings Object: The Strings object provides methods and attributes for maintaining a string safely. The idea with the Strings object was to control the allocation size for a string to ensure that nobody overflows the buffer and causes big problems. It was designed to contain most methods so the coder rarely needed to touch char pointers. You should never write to a char string that is in a Strings object without using one of the methods, as it could really cause problems. If you do, do so with care. Timespan Object: The Timespan object is used to keep track of the time elapsed since the object was created. It can be used to determine how long it takes a player to run through a hallway full of flying spears. I use it to keep track of how long it takes to do object lookups in Object_List and other database lookups to ensure the code is efficient. Object Hierarchy: All game objects that the player sees are represented by many different classes of objects. From Locations to Weapons and Mobiles, each has a parent class and its own attributes. At the top of the object hierarchy is the Entity object, which includes all the game objects. The following represents the heirarchy for all mud objects in the game. Entity | ------------------------------------------------------------------------ | | | | | | | | Ability Specials Quest MudObject Action Level Text Verb | | | | ---------- ------------------------------------------ | | | | | | Spell Skill Location Item Individual Builder | | | ------------ | | | | Mobile Player | -------------------------- | | | Marker Moveable Door | ------------------------------------------------------ | | | | | | | Boat Book Key Merger Rope Weapon Wearable | ----------- | | Food Money Entity Object: The Entity object contains a name attribute used to provide a unique identifier for the particular entity in the area. If you call a moveable pebble1, it is the only entity in that area that can be pebble1 (Other areas can have pebble1 entities). The main purpose of the Entity class is to provide a similarity between objects. This allows us to have areas filled with many different types of objects, with only one type of pointer (Entity *). Under Entity, you will find various objects that really are not "in" the game per-say, but the game does use. The reasons they are classified as Entity is so that we can edit them on the builder port by loading them into an Area_Dbase. The Text object is a good example of this. It is merely a way to load help, info, and banner text files onto the game port and modify them. When the builder commits, the help files would be written to the help directory. In the game port, the help files are read directly from the file and never loaded into a Text object. In fact, the Text object is never used on the game port. Quests, Levels, Spells, Skills, Actions, Specials and Text objects are all objects that can be loaded in the builder port and edited. See the builder tutorial for a description of each object and what it is used for. MudObject Object: MudObjects are really the "meat" of the mud. They are what the player interacts with during gameplay. In fact, even the player is a MudObject. The MudObject class contains all the attributes and methods that you would find in all MudObjects, such as location of the object and title for that particular object. During gameplay, the players never see the actual name of the MudObject. That is only for the mud engine. The title is what they actually see, so when referring to objects, you should always refer to the title instead of the name. The MudObject holds the specials attached, which execute whenever a certain "trigger" is met. You will have to write in a command to perform a special check for a certain trigger (the check_specials command in utils.cc) in the player or builder command. MudObjects also hold the inventory of that object. Using the LinkedList object, it maintains a linked list of MudObjects that are in the object. When a player enters a location, they are added to that location's linked list. When a player gets something, it is added to that player's linked list. All Other Objects: The MudObject consists of four different subtypes (two of which have several subtypes of its own). The Builder really isn't a part of the gameplay and will probably be moved under Entity down the road. Location defines the rooms a player can go to and has attributes to provide links to other locations in all directions. Individual defines anything that walks around the mud, from a Player to a Mobile (creature defined by the builders). Finally, the Item object defines any inanimate object ranging from a Door to a Book. The Item object consists of three types. The Marker object (used for examinable objects that you can't pick up...a tree for example) and the Door object are both objects that can't be picked up. A Moveable object is any other object that can be picked up and placed into the player's inventory. All objects in the Moveable object subclasses are described best in the builder tutorial. The Merger object and subclasses are unique in that only one object will be in any inventory at a given time. The code ensures this. So if you have a Money object of coins (marked with 50, indicating you have 50 of these when actually you only have one object marked as 50) and you get an object of coins(30), it will increase the number on your carried coins object by 30 and delete the one marked 30. So now you will have one object in your inventory called coins and marked 80. Hence where the name came from, Merger. The objects merge and divide as necessary to ensure there is only one in a specific inventory. Windows Code: The windows code is all stored in win32 directory (with a few segments inside the main src code). The main dialog box is taken care of in aime_w32Dlg.cpp which handles the messages. A timer is set up to run the mud which fires every couple of times per second looping through the mud for events. It also checks for user connections and handles user input and output that has been stored in the buffers. During a bootstrap, the timers will progressively run more code in the bootstrap function (located in mud.cpp) each time the timer code fires. This makes it so output will show in the output window of the dialog box and the user can halt the bootstrap at any time. They also will have the opportunity if the need arises to convert data files or create directories. In the linux version, all this is handled in main.cpp by a program loop. Summary: This was a basic overview of the objects in the code and the main design of the mud. It should help you get started on coding things into the mud. For more details on the specifics of each object, you can check the code. If there is anything that is not documented to your liking or you don't understand and would like it worded a different way, please let Slate know at noelg@acm.org. The mud should be documented so that it isn't too difficult for anyone to write code for it and if not, it should be corrected. Any comments or suggestions for the code, something that you think could be done better another way, please let Slate know (noelg@acm.org). Thanks and have fun!