Posted by
| Nick Gammon
Australia (23,133 posts) Bio
Forum Administrator |
Message
| With the imminent release of the MUSHclient source code, I thought it would be useful to write a bit of an explanation of how the source is organized.
The source consists of something like 400 .cpp and .h files, and it could be a bit overwhelming trying to work out which part holds the "important bits" without some sort of guideline.
When someone comes to me with a request for a new feature, or new script routine, there are some files I automatically go to initially, I will try to describe those below.
MUSHclient was written using MFC (Microsoft Foundation Class) libraries - a thorough understanding of those will be very helpful in understanding how many things work.
Also, because of the use of STL, it helps to understand that as well.
It was compiled with Microsoft Visual Studio Version 6.0 with Service Pack 6 installed.
A note on style
MUSHclient has been a work in progress since late 1995. My style has changed somewhat in that time, plus as the program complexity has increased some parts have become semi-redundant.
For example, there is code to return the name of the current world:
// world.worldname - returns the name of the current world
BSTR CMUSHclientDoc::WorldName()
{
CString strResult;
strResult = m_mush_name;
return strResult.AllocSysString();
}
However after a while it became obvious that there would be lots of little "get information" functions, and thus the GetInfo function was born. Now, you can use GetInfo (2) to achieve the same result. Thus, the earlier WorldName was only retained for backwards compatibility.
There are also some places where things which are similar are done in different ways (eg. Macros / Accelerators). These are examples of where a simpler method, which I initially used, has been replaced by a more powerful method.
Lists / maps / strings
There are extensive lists / vectors / maps in the code, as well as "string" classes. Initially when MUSHclient was developed I used the MFC versions of these. For example, a list of strings might be:
CStringList m_strIncludeFileList; // list of root level include files
More recently I started using STL (Standard Template Library), and where convenient, used the STL containers instead. For example:
deque<string> m_sRecentLines; // for multi-line triggers
Iterating through an MFC list generally looks like this:
for (POSITION pos = m_PluginList.GetHeadPosition(); // get first position
pos; // check if we are at the end yet
)
{
CPlugin * p = m_PluginList.GetNext (pos); // get current, advance to next
// do something with the plugin (*p) here
}
However iterating through an STL container looks like this:
for (map<string, string>::const_iterator iter = sometable.begin (); // get first position
iter != sometable.end (); // check if we are at the end yet
iter++) // advance to next
{
const char * key = iter->first.c_str (); // key of map
const char * value = iter->second.c_str (); // value associated with key
} // end of doing each one
Strings are generally stored in the MFC CString class, however some parts of the code now use the STL string class.
File naming conventions
- Generally a file named like this "xxxxxDlg.cpp" is the implementation for a dialog box, and the corresponding "xxxxxDlg.h" file is the header file for that dialog.
For example, SpellCheckDlg.cpp implements the CSpellCheckDlg class (note the "C" in front of the class name).
- Files to do with MXP processing are prefixed mxpXXXX, for example: mxp_phases.cpp, mxpCloseAtomic.cpp etc.
- Files for loading/saving the XML data (eg. world files) are named xmlXXXX, for example: xml_load_world.cpp, xml_save_world.cpp.
- Plugin-related files are named PluginXXXX, eg. plugins.cpp, PluginWizard.cpp, PluginWizardSheet.cpp.
- Various Lua-related files are named lua_XXXX, eg. lua_methods.cpp, lua_scripting.cpp, etc.
Major files
- MUSHclient application (CMUSHclientApp):
- MUSHclient.cpp
- MUSHclient.h
This implements application-wide code, particularly program startup, in the function:
BOOL CMUSHclientApp::InitInstance()
{
// start up here
}
This does stuff like basic initialization, parsing command-line options, and opening worlds in the startup list.
Inside MUSHclient.h are the global preferences, eg.
unsigned int m_bAllTypingToCommandWindow;
unsigned int m_bAlwaysOnTop;
unsigned int m_bAppendToLogFiles;
unsigned int m_bAutoConnectWorlds;
The code for loading/saving global preferences is in globalregistryoptions.cpp.
- World document (CMUSHclientDoc):
This is the basic "connected world" processing. There are a lot of functions in doc.cpp, including connecting to a world, disconnecting, handling a packet from the MUD, and so on. In particular, processing of input is done in a "state machine" to handle telnet sequences (and MXP sequences) that might span multiple packets. Some of the telnet state machine is in telnet_phases.cpp.
The constructor code for doc.cpp has been moved to doc_construct.cpp, because there is so much of it.
- Other types (triggers / aliases / timers / plugins):
This file implements the declarations for commonly-used things like:
- Lines (in the output window)
- Style runs
- Actions (eg. MXP actions)
- Aliases
- Triggers
- Timers
- Variables
- Plugins
- Notepad document (CTextDocument):
- TextDocument.cpp
- TextDocument.h
This is the code for the internal notepad.
- Activity window (CActivityDoc):
- ActivityDoc.cpp
- ActivityDoc.h
This is the small "list of active worlds" window.
- General text and other utilities:
- Script methods:
- methods.cpp
- world_debug.cpp
- lua_methods.cpp
- functionlist.cpp
This rather large file (methods.cpp) implements almost all of the script functions. If you wanted to add another script function this would be the file to put it into. The "Debug" script function is implemented in its own file (world_debug.cpp).
The file lua_methods.cpp implements "glue" routines, that make a Lua interface to the script functions in methods.cpp.
For example, the Accelerator function:
//----------------------------------------
// world.Accelerator
//----------------------------------------
static int L_Accelerator (lua_State *L)
{
CMUSHclientDoc *pDoc = doc (L);
lua_pushnumber (L, pDoc->Accelerator (
my_checkstring (L, 1), // Key
my_checkstring (L, 2) // Send
));
return 1; // number of result fields
} // end of L_Accelerator
This effectively calls the "real" Accelerator function for the correct world, passing two arguments (key, and what to send), and returns the number, being the result code from Accelerator.
The function "doc" attempts to find the correct document pointer - with Lua you can get a document other than the current one like this:
otherworld = GetWorld (name)
if otherworld == nil then
Note ("World " .. name .. " is not open")
return
end
Send (otherworld, message)
-- alternative syntax: otherworld:Send (message)
Because of this, we have to check the first argument to the function call to see if it is a world userdata, and if so use that, otherwise default to the current world.
There is a subtle problem with reporting the argument number if an argument is incorrect, because of the optional first argument, hence my_checkstring is a stub routine that works similarly to luaL_checkstring, but handles the problem with argument numbering.
The file functionlist.cpp lists all known function names, for use with the help functions that do function-name completion, and so on.
- End of line processing:
An extensive amount of processing occurs at end-of-line, thus this is placed in its own file. Stuff like trigger processing, line logging, line recolouring is done here.
- Command evaluation:
This is where command evaluation is done (eg. alias processing).
- Output window:
This handles drawing of the output window, handling mouse clicks, and menu commands relevant to the output window.
- Command window:
This handles the command window - things like up-arrow, down-arrow, and related menu commands if the focus is in the command window. It also catches the <enter> key being pressed and works out whether to do a script, auto-say, etc.
- Configuration:
- CommandOptionsDlg.cpp
- configuration.cpp
- genpropertypage.cpp
- GlobalPrefs.cpp
- GlobalPrefsSheet.cpp
- PrefsPropertyPages.cpp
- PrefsPropertySheet.cpp
- scriptingoptions.cpp
- TreePropertySheet.cpp
These handle the global and world-based configuration dialogs and other processing.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|