1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
|
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 <name>.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!
|