Hitchhiker's guide thru psyced source
When you first look at psyced code you may be quite confused,
but a few words will help you find yourself around. most funny looking
things have a good reason to be the way they are.
- LPC runs in a "sandbox" that starts in the "world" directory,
like a less strict chroot environment, so all the real code is in there,
although a few symlinks lead back to the top where the files for your
local administration are placed, like the "config" and "place" directories.
- Don't be confused by the fact that traditional LPC drivers keep
their LPC files with a ".c" suffix and data files with a ".o" suffix.
This is obviously something that should be changed one day, but it
is actually tricky to do in the driver source code, so we leave it to
Lars to wonder if it's worth the trouble. :°)
- We have put the heart of the psyced library into "net", presuming
that, should other LPC projects like MUDs want to merge psyced into
their system, psyced would take over several networking jobs for them.
"drivers" contains the driver abstraction kits, although we currently
support psyclpc and LDMUD. it does NOT contain the driver itself.
"default" contains the text database. you can think of it as
a more advanced form of gettext. it is serviced by "net/text.c".
For each ".textdb" file a corresponding "/text" object is cloned, thus
reloading one of them goes something like "/reload default/en/plain/text"
- We have avoided mentioning these paths literally in the code, in
case you want to use the code in different directory names. That's why
the code has so many NET_PATH and similar #define macros in it.
- All psyced code uses #include <net.h>. Reading that file and
the files it includes first, will answer a lot of questions,
like ME being the macro that points to the current object
(could have been "this") and MYNICK and MYLOWERNICK containing the
nickname of the current entity. UNIFORM(sender) will always produce
the string uniform for a sender. HTTPS_URL and HTTP_URL contain
the prefixes to the built-in webserver, if activated.
- You can distinguish system functions from object methods by their
naming style. Methods defined within object classes do not contain
underscores in their names, but have the second word capitalized
instead, as in "createUser()" or "isNewbie()". Whereas system functions,
no matter if provided by the driver like "lower_case()" and "convert_charset()",
or if implemented in the psyced library like "register_target()" or
"find_place()", have underscores in their names. Only one-word-names
like "create()" or "sendmsg()" are admittedly impossible to distinguish.
- Now you think we could use capitalized names, but we prefer to keep
those for complex data structures like the struct for parsed URLs as
defined in url.h. It allows you to access parts of a uniform by name
as in "uniform[UHost]" and "uniform[UScheme]". grep for UScheme to find
examples.
- Completely uppercased words are used by preprocessor macros, as
normal with C programming. They can be extremely powerful and save us
from a lot of blues we would otherwise have in languages like perl or java.
- Our debugging macros are a sublime example of the power of the preprocessor.
Just look at debug.h to find how the debug levels are converted into Px macros.
P0 thru P4 print messages to the console depending on the debug level
provided at system start. They are used as follows:
P2(("%O got %O which should have been %O instead!\n", ME, this, that))
If the debug level is 1, the complete statement will simply not exist.
The Px-macros use sprintf inside, which behaves much like C sprintf
except for %O which lets you output any variable safely. There is also
a PT macro which is for temporary debugging. It will *always* show on
your development system and *never* show in a productive environment.
Similar to the Px-macros there are also Dx-macros. They let you construct
more complex statements, like
D2( if (this != that) PP("%O: %O != %O\n", ME, this, that); )
and you can always "#if DEBUG > 2" directly, whenever you need to
enclose several lines into conditional debug compilation. The S() macro
is just a synonym for sprintf() and should not be used for real code.
When working on psyced please add -DDEVELOPMENT to your start scripts
so you can see messages generated by PT() and DT() as described before.
- You can apply varying degrees of debug level to specific parts of psyced,
like for example the textdb subsystem, by adding -DDtext=2 to the psyclpc
flags in psyced.ini. You can figure out which other parts of psyced are
debuggable by doing a "grep -r 'define DEBUG D' ." in the world directory,
then assign a debug level in the same way as the global debug level described
above.
- We admittedly use ifdef/else/endif preprocessor constructs too much, we
keep all sorts of dysfunctional code in it, maybe because one day we want to
make it work, or we want to learn from it, or we want to keep ourselves from
doing same old mistakes twice. Still all this ifdef'd out stuff stands in
the way of getting started with psyced and getting a complete view of the
code as it is in use. Sending those code fragments into repository history
will probably delete it from consciousness and make us do old mistakes
twice. A different approach to this is to use folding editors. Since all
psyced developers are vim users, we have added "fold markers" to make
inactive parts of code disappear. Should you be using a different editor
to look at psyced files, try folding away anything between {{{ and }}}.
- The text database is a major source of voodoo in the psyced
system. You can use it by including text.h. This will make you inherit
the text client class textc.c, and define a T(mc, default) macro.
You can specify which language or variation of the text database to
use by specifying sTextPath(), then access the elements using the
PSYC-typical message code hierarchy, as in T("_notice_session_end", "Boom!").
If you also need the PSYC-typical variable replacements in a template, you
need to use the library function "psyctext()", which also gets used by
the user output method "w()".
- Perl/php's hashes in LPC are called mappings. Similar to objects in
ECMAscript. See the LPC documentation
for details about them. They are in fact not implemented by means of
hash tables, in order to save memory.
- In LPC, global variables in object classes are never accessible from
outside. This is good programming design and I am glad this is enforced.
So whenever you need to access somebody else's data, you need to use a
properly set up interface method. They are typically called "qSomething()"
to query a variable and "sSomething(value)" to set a variable to a certain
value.
- But since objects are usually also PSYC entities we hardly have any
traditional inter-object communication using the -> operator (also
called call_other in LPC-speak). Instead the internal PSYC API is used,
which brings a PSYC message from a source to a target. Only when the target
is outside the psyced, the message will be rendered into actual PSYC protocol.
To send a message the library function sendmsg is used as follows:
varargs int sendmsg(mixed target, string mc, mixed data, mapping vars);
and similarely the reception method that every PSYC entity class implements:
msg(mixed source, string mc, mixed data, mapping vars)
Here's a typical example on how to transmit a message:
sendmsg(target, "_request_version", "[_nick] requests your version.",
([ "_nick" : MYNICK ]));
- There is a special set of variables which are by definition persistent.
They are defined in storage.c and are always accessed using the "v()" macro
as in v("name"). They typically hold the properties of a person or a place,
and since they may one day come from an external data base, no direct
access to the underlying mapping is granted. Instead you can "vSet()" or
"vDel()" them. When accessing them in an other object, you can't use
"->v()" so we had to introduce "->vQuery()".
- Global variables can however have modifiers. Most of them are
declared "volatile" as they would by default get persisted away, and
we don't want that. Persistent data should almost always be put into v().