Imported version 3.2.12 from PRCS.

git-svn-id: svn://svn.bearnip.com/ldmud/3.2/trunk@9 56160b6e-f8f8-0310-bd54-bccebfe6a2f2
This commit is contained in:
Lars 2005-06-21 02:30:03 +00:00
commit 77996da5fc
1602 changed files with 278250 additions and 0 deletions

6683
CHANGELOG Normal file

File diff suppressed because it is too large Load diff

481
COPYING.LIB Normal file
View file

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

57
COPYRIGHT Normal file
View file

@ -0,0 +1,57 @@
LDMud, a LPmud gamedriver.
Copyright 1997-2004 by Lars Duening.
LDMud is based on LPmud, which is
Copyright 1989-1991 by Lars Pensj|, and, starting with version 3.1.2,
Copyright 1991-1997 by Joern Rennecke.
Source code herein refers to the source code, and any executables
created from the same source code.
All rights reserved. Permission is granted to use, extend and modify the
source code provided subject to the restriction that the source code may
not be used in any way whatsoever for monetary gain.
------------------------------------------------------------------------
LDMud contains contributions of many volunteers as listed in the file CREDITS.
Of these, the following packages are subject of their own copyright when
used outside the context of this gamedriver; please refer to the appropriate
files for details.
The regexp package is based on the code written by Henry Spencer.
The portable crypt() implementation is Copyright by Eric Young.
The smalloc allocator was written by Sean T. Barrett and put into the
public domain.
The 'ed' editor (with the exception of the indentation code) is Copyright
by Brian Beattie, Kees Bot and others.
The non-corrupting indentation code for the 'ed' editor was written and
contributed by Felix A. Croes (<felix@dworkin.nl>) from his DGD gamedriver.
The sprintf() efun was written by Sean A. Reith and put into the public
domain.
All code taken from the MudOS driver is Copyright by Tim Hollebeek and
the original authors. Use for other purposes than LPmud drivers or under
licenses significantly different from the original LPmud license needs
explicite permission.
The random() efun is implemented using the Mersenne Twister generator which is
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, and subject
to a BSD-style license. The implementation is the faster implementation based
on Shawn Cokus and Matthew Belle's improvements. See random.c for the full
text of the license.
The xerq demon is Copyright (C) 1995 by Brian Gerst. Use of the source
code is subject to the restriction that it must not be used for monetary
gain.
The MD5 message digest algorithm is Copyright (C) 1991-2, RSA Data Security,
Inc.
The lpc-mode.el for EMACS is Copyright (C) 2002 Vivek Dasmohapatra
<vivek@etla.org>, and distributed under the GPL.

262
CREDITS Normal file
View file

@ -0,0 +1,262 @@
The program was originally written by
Lars Pensj|, April 1989 (lars@cd.chalmers.se (no longer valid)).
Lars gives the following credit:
The regexp package was made by Henry Spencer.
The ed package was not done by me. See the file ed.c for information.
Bit manipulations was implemented by pell@lysator.liu.se who also found
several bugs that had eluded me for months.
Valuable help and hints from: Mark Nagel, James Seidman, Moonchilde and more.
Roland Dunkerley III fixed for-loops, do-while and the operators ++ and --.
Sean T Barrett made smalloc.c.
Lennart Augustsson convinced me to implement a compiler for a virtual
stack machine. He also implemented the built-in preprocessor.
John S. Price found and fixed many bugs.
The shadow idea was "forwarded" to me by John S. Price from Team Cthulhu
(Bill Burdick, Roy Riggs, Mitch Adler).
Michael Malone, Michael O'Reilly, Klaus Rennecke, J|rn Rennecke and
Germano Caronni found and fixed a large amount of bugs.
Petri Wessman fixed changes for SCO unix and AIX.
-----------------------------------------------------------------------------
Joern Rennecke ( amylaar@cs.tu-berlin.de ) gives this credit:
The non-corrupting indentation code for ed was written by Felix A. Croes,
aka Dworkin (<felix@dworkin.nl>) and originates from his DGD.
Mud-sprintf() is from Lynscar (Sean A Reith, <gusar@uniwa.oz.au> and
was donated into the Public Domain.
The alist based mapping implementation was supplied by Dworkin.
The idea for query_actions and an implementation that has been partially used
is from Peter Eriksson (aka lpd) pen@lysator.liu.se
The port to Amiga was done by Mateese ( Lars Duening )
The udp connectivity is from the CD driver, and was fitted in by deepthought.
Demos, Freaky, Hurl, Hyp, Mateese, Pepel and Jof did a lot of alpha
testing.
Reimer Behrends located a lot of bugs. He also supplied a portable macro to
test numbers for having but a single bit set, a mergesort based
sort_array() and variadic function support.
get_object_actions() was supplied by herp.
Marcus Meissner found and fixed many bugs.
Carl White, Mark (ec179@city.ac.uk), Archimedes (Robert Fuller), Eric Blade,
Mitchell Brandsma, Sam Bobroff, Jim Cogbill, Ben Fennema,
Sorva (Ed Zielinski), Loke@Wonderland (Solem), Claus S. Jensen, Nils Magnus,
samo@MorgenGrauen, Frobozz (Bryan Schuler), Sique@Nightfall, APJ,
Deathknight, Tero Jyri Michael Pelander (tpeland@utu.fi),
Willem van der Oord (willem@dcr.net), Hate@MorgenGrauen,
Bob Farmer, Randor@EoP, Trevor Phillips, Robert M. Zigweid, Inspiral,
Brian Gerst, Rasmus H. Hansen, Demodian (John Kurtz), William J. Burton,
Patrick Li, Niilo Paasivirta, Frank Stolze, Penelope@Avalon,
Nathan@Unholy Lands, mubo@SiliconDream, Sunblood@Tubmud, Alfe@Tubmud,
Tatu Saloranta, Chameloid@Tubmud and Martin Brenner (deepthought) reported bugs.
Foslay did the OS/2 port.
Ray Gilbert originally did the MSDOS port, which was modified later by
Rasmus Hellbug Hansen.
Marcus Meissner contributed regreplace().
Slider@Tubmud supplied GnuWin32 patches.
-----------------------------------------------------------------------------
Lars Duening thanks:
- Dana Durkee and the magnificient Wessex for enhancing my life.
- the citizens of One Step Beyond, PK-Mud and UNItopia for enduring my
driver experiments.
- Malcom Tester (malcom.tester@comcast.net) for providing me with access to
a Solaris system for testing.
- Elric (elric@lpmud.com) for providing me with access to
a FreeBSD system for testing.
- Blackcat (?) for providing me with access to a Linux system for testing.
- Freaky for providing me with access to an Irix system for testing, numerous
bug reports, and lots of patience when new versions crashed even faster
than the old ones.
- Kees Leune for providing me with access to a Linux/Sparc system for
testing.
- Stefan Petri and contributors for the extensive documentation of the
Amylaar driver (see also doc/README).
- the MudOS programmers for the efun terminal_colour() and Michael Schaefer
for adapting it for LDMud.
- Martin Brenner: initial multi-port implementation, inetd compatibility
code.
- Brian Gerst (bgerst@quark.vpplus.com) for the xerq daemon.
- Reinhard Pfau (Reinhard.Pfau@unix-ag.org, Mubo@SiliconDream) brought
the autoconf mechanism up to date.
- Ron Koerner (ron.koerner@gmx.de, Righ@FinalFrontier) provided the
initial implementation of inline closures modelled after MudOS' syntax.
- Uwe (uwe@6bone.informatik.uni-leipzig.de) provided the basic IPv6 support.
- Mark Daniel Reidel (Karm@Nightfall) did the first implementation of
the mySQL efuns.
- Dennis VanHoey (devanhoe@usol.com) did the compiling and testing for the
Darwin port.
- Bart van Driessche (bart@multi.franken.de, Hunter@FinalFrontier) did the
new OS/2 port.
- Menaures added the tab character conversion to the editor, and gleefully
found new ways to crash the driver.
- Acius donated the LPC implementation of the A* pathfinding algorithm.
- for bug reports, critics, fixes, patches and other support:
Acius (acius@bigfoot.com)
Alex@TubMud (at16@st-andrews.ac.uk)
Alexander Fetke (alfebtcd@cs.tu-berlin.de, Alfe@Tubmud)
Alexander Dobrinescu (ad@artifacts.de, Lev@FinalFrontier)
Anders Strandloev Elkjaer (ase@cs.auc.dk)
Andre Meiske (guandalug@magellan.tat.physik.uni-tuebingen.de)
Andreas Grammlich (andreas.grammlich@prepress.de, Gum@Wunderland)
Andre Grosse (ag17@irz.inf.tu-dresden.de, Patryn@Morgengrauen)
Andreas Fischer (andreas.fischer@physik.tu-chemnitz.de)
Andreas Klauer (menaures@metamorpher.de, Menaures@Unitopia)
Andreas Weller (aw6@informatik.uni-ulm.de, Zwirch@Carledon)
Andres Cvitkovich (ac@twig.mond.at, ac@zwerg.at, Woody@Silberland)
Andrew Weisz (elarke01@snet.net)
Andy Zeneski (jaz@zsolv.com)
Arne @ Elridion
Athanasia Steele (azurite@mail1.nai.net)
Baba@Avalon
Bastian Hoyer (hoyer@wisc.de, dafire@dafire.de)
Ben Fennema (bfennema@ix.netcom.com)
Bertram Felgenhauer (bf3@irz.inf.uni-dresden.de, Fuchur@Wunderland)
Bill Andrulonis (bill.andrulonis@citrix.com, Bofin@Realmsmud)
Blackcat@PK-Mud (amuelle1@htwm.de)
Brandon (pothole@darkspyre.quad.net)
Brandon Mathis (brandon.mathis@medsolutions.com)
Bruce Mitchell (mitchell@manly.delconet.com, Klahn@Manly Mud)
Carlo von Loesch (cvl@symlynX.com, lynx@high.impact.pages.de)
Casey Zacek (cz@netspire.net, zippo@eotl)
Christian "Leonidas" Becker (cbecker@informatik.hu-berlin.de)
Christian Kirschbaum (chrissy@cs.tu-berlin.de, Chameloid@Tubmud)
Christian Mudra (c_mudra@informatik.uni-kl.de, Coogan@Tubmud)
Christian Welzel (Sir_Gawain@gmx.de, Gawain@Regenbogen)
Collin Baillie (sanac@optushome.com.au)
Craig Venz (Craig Venz@citrix.com, Kilan@Realmsmud)
croft@UNItopia.rus.uni-stuttgart.de
Dale "Elric" Perkins (dale_perkins@mac.com, dale@lpmud.com)
Daniel von Dincklage (daniel@ozet.de, Sunblade@SiliconDream),
Daniel Fischer (dfischer@gmx.de, Dan@Gueldenland)
Daniel Sloan (sloand@alphalink.com.au)
Daryth Kendrickson (stavros@deadofnight.com)
David Gibbs (ishara@btinternet.com)
David 'Greyelk' Gibby (oop_apprentice@yahoo.com)
Dennis VanHoey (dvanhoe@usol.com)
Dominik Zanettin (zhan@opossum.htu.tuwien.ac.at, Zhan@Beutelland)
Enrico Bauermeister (ebauermeister@t-online.de, Enrico@FinalFrontier)
Eivind (ekj@ekj.vestdata.no)
Erik Meusel (emeusel@prinz-atm.CS.Uni-Magdeburg.De)
Fantom (Fantom@Tharsis-Gate.org)
Fini Jastrow (Fiona@Wunderland, ujastrow@mail.desy.de)
Florian Heinz (ctx@amail.dereference.de)
Frank Kirschner (freaky@unitopia.uni-stuttgart.de)
Frank Stolze (fstolze@iName.com)
Frank Syse (syseworks@mac.com)
Gerhard Schmidt (estartu@augusta.de)
Gimli@Unitopa (gimli@schlumpf.net)
"Gnomi" (gnomi@UNItopia.rus.uni-stuttgart.de)
Gunnar Kreitz (gunnark@chello.se)
Heiko Kopp (hkopp@informatik.uni-rostock.de, bardioc@bardioc.com,
Bardioc@Evermore)
Henk Stokhorst (Tha@wxs.nl)
Holger Kremss (jur94ewp@studserv.uni-leipzig.de, Holger@web.de,
Holger@Wunderland)
Jason Hitt (jhitt25@swbell.net)
Joern Rennecke (amylaar@cygnus.co.uk)
Jose Manuel Roman (roman@wol.es, Theuzifan uzi@simauria.upv.es)
Joseph Graham (randalar@earthlink.net)
Josh Rollyson (jrollyson@sosdg.org)
Juergen Weickert (Sissi@UNItopia)
Karl N. Matthias (karmat@matthias.org)
Kathrin Paschen (paschen@ira.uka.de)
Kees Leune (c.j.leune@kub.nl)
Kirnan@Avalon
Krystal-Anne Rubenstein (punkette@hotmail.com)
Largo@Wunderland (emeusel@csmd.cs.uni-magdeburg.de)
Lasse Fuglsang Pedersen (volmer@qeep.dk, Zordiac@DeeperTrouble2)
Lee Salzman (lee.salzman@lvdi.net)
Logic (logic@logic.net)
"Mammi" (mammi@unitopia.uni-stuttgart.de)
Manu Mäki (mtmaki@pultti.turkuai.fi, Cendor@Winterkill)
Mark Daniel Reidel (Karm@Nightfall,
reidelm@rbg.informatik.tu-darmstadt.de)
Markus Peter (warp@spin.de, warp@hip.spin.de)
Martin (mandragon@morgengrauen)
Mat@Maethos
Mats Carlberg (matca@lysator.liu.se)
Matthew Julius (julius.2@wright.edu)
Michael Fedrowitz (mfedrowi@ix.urz.uni-heidelberg.de,
Michael_Fedrowiz@gmx.net, Rhodry@ForgottenDreams)
Michael Sporn (sporn@mathematik.hu-berlin.de, sunblood@tubmud)
Mike Conley (mpc@sgi.net, Tamarindo@Sovereign)
mudder@Furtual.ORG
"Nathan@Unholy Lands" (mud@pc181.iaee.tuwien.ac.at)
Neil McBride (neil@castinian.org)
Nepomuck@UNItopia
Nick Derewianka (nickd@e-media.com.au)
Nico Ehemann (n.ehemann@wzl.rwth-aachen.de)
Niels Boehm (bohmmrwc@minet.uni-jena.de, Mariko@Regenbogen)
Oskar Linde (d98-oli@nada.kth.se)
Parsec
Patrick May (patrick.may@cedelgroup.com)
Paul Apgar (tiresius@usa.net, tiresius@Cosmos)
Rainer Doerntge (rainer.doerntge@dlh.de)
Rajko Albrecht (ral@alwins-word.de, Alwin@Avalon)
Ralph (ralphi@vkr.k-net.dk)
Ralph Zitz (RZ@zitz.dk, Nostradamus@OSB)
Reimer Behrends (Macbeth@Tubmud)
Reinhard Pfau (reinhard@netlab1.hrz.uni-siegen.de, mubo@SiliconDream),
Reinhard Vicinus (vicinus@rhrk.uni-kl.de, Risky@Unitopia)
Robb Townsend (meecham@infonline.net)
Robert Forshaw (postmaster@rbf.org.uk)
Robert Reither (mud@klein.htu.tuwien.ac.at, Nathan@Unholy.Lands)
Rodney Quaye (sark@oberon.krans.com)
Ron Dawson (rondawson@syd.eastlink.ca, rdawson@cgc.ns.ca, Caper@PixieMud)
Sebastian Huss (huss@informatik.hu-berlin.de, baba@avalon)
Slava "DieHARD" Ignatjev (root@impar.com, slava@intersfera.lt)
Sieni (sampsa@netsonic.fi)
Simon Fischer (firebat@redcrown.de)
Stefan Budweiser (Stefan.Budweiser@compaq.com)
Stefan Riemer (stefan.riemer@bauing.uni-weimar.de, Peng@FinalFrontier)
Stephan Lichtenhagen (lichti@amethyst.chemie.uni-dortmund.de,
stephan@lichtenhagen.de, Tiamak@Morgengrauen)
Steve Goucher (su2996@eclipse.co.uk)
Steve Willoughby (steve@alchemy.com, Ragnarok)
Tatu P. Saloranta (doomdark@cc.hut.fi)
Terry Penn (terry@simaril.net)
TheFinn (thefinn@ar.com.au)
Thomas Feldmeier (tfeldmei@student.informatik.uni-ulm.de, Zora@Carledon,
zora@alpha.universalware.de)
Thorsten Klose (Thorsten.Klose@infineon.com, Avatar@avalon)
Tiberian@Unitopia
Tmm@UNItopia? (Tmm@gmx.de)
Tobias Josefowitz (tobij@goodadvice.pages.de)
Tomas (Vandergnome@aol.com)
Tomi Valkeinen (tomi.valkeinen@taikatech.com, Tomba@Batmud)
Tucita@UNItopia
Urv (urv@hispeed.ch)
Volker Behr (Sorcerer@AbendDaemmerung, vrbehr@cip.physik.uni-wuerzburg.de)
Wayne Woods (w.woods@optusnet.com.au)
Willem van der Oort (willem@dcr.net)
Wolf Dieter Dallinger (wolf.dieter@dallinger.de)
Zonk@FinalFrontier (zonk@finalfrontier.mud.de, rueschi@berit-broese.de)

1329
HISTORY Normal file

File diff suppressed because it is too large Load diff

298
INSTALL Normal file
View file

@ -0,0 +1,298 @@
Topics covered:
- Unix or Unix-like systems, with additional details for FreeBSD, AIX 3.4,
and DEC Ultrix.
- IPv 6
- mySQL
- BeOS
- Windows 95/98/NT
- OS/2
Unix or Unix-like system
------------------------
The driver uses a standard autoconfiguration system which on most
systems does all the work for you (for exceptions see below).
To prepare the compilation, execute the 'configure' script from
within the src/ directory.
configure checks for a number of site specific settings and uses this
information to create the files machine.h (from machine.h.in), Makefile
(from Makefile.in) and config.h (from config.h.in). We'll come back to
config.h below.
configure takes a lot of arguments (--help will tell you everything),
but the most important are these:
--prefix=PREFIX: the base directory for the mud installation,
defaults to /usr/local/mud .
--bindir=DIR: the directory to install the executables in,
defaults to ${PREFIX}/bin .
--libdir=DIR: the directory where the mudlib is found,
defaults to ${PREFIX}/lib .
--libexecdir=DIR: the directory where the programs for the ERQ are found,
defaults to ${PREFIX}/libexec .
These settings are written into the Makefile and compiled into the driver,
just the mudlib directory setting can be changed with a commandline
argument.
A lot of the drivers parameters can be tweaked for better performance; these
parameters are defined in config.h . This file too is created by configure,
which provides sensible defaults for all parameters for which no explicite
setting is provided. To tweak a setting yourself, pass the argument
'--enable-<option>=yes|no' resp. '--with-<option>=<value>' to configure on
the commandline.
Alternatively, the indivial specifications can be collected in a settings
file, which is stored in the directory src/settings/. To use the
setting file <osb>, give '--with-<osb>' as argument to configure. The
file src/settings/default documents the available settings. The setting
files are self-executing: './settings/<foo> [<extra-configure-args>]' will
start configure with the proper commandline arguments.
The following environment variables can be used to tweak the behaviour
of the configure script:
CC: the name of the C compiler
CFLAGS: compiler flags to be used during the configure script
EXTRA_CFLAGS: compiler flags to be used when compiling the game driver
LDFLAGS: linker flags to be used by configure and for linking the
game driver.
After configuration is finished, you may want to modify the Makefile
to fine tune those parameters which are not covered by the configuration.
The compilation is done using make. Following targets are implemented:
<none>: compile the driver, named 'ldmud'.
install: compile the driver and install it in ${bindir}
utils: compile the utilities, especially the ERQ demon
install-utils: compile and install the utilities in ${bindir}
To actually run a mud, you need a mudlib. The driver comes with the
source of the old 2.4.5 mudlib in mud/lp-245/, support for other mudlibs
is also found in mud/. Copy all mudlib files into your 'lib' directory
and make sure that the files from mudlib/sys/ all go into your lib's
include directory (usually 'sys/').
To test the driver, start it with 'ldmud &'. If you see the message
'LDMud ready for users', the driver is up and accepting connections. Test
it with 'telnet localhost 4242'.
pthreads:
If your systems supports pthreads, --enable-use-pthreads will allow to
compile the driver with pthread support. Currently this means that
background threads will be used to write data to the network.
WARNING: pthreads support is still experimental and might crash
your driver! I MEAN IT!
FreeBSD:
When using gcc, it can happen that the compiler
aborts with signal 10 or 11. The reasons are unknown, but you can
restart the compilation process by typing "make" again (and again...).
AIX 3.4:
The native compiler comes in several forms, of which only the
basic form 'xlc' works with the configuration script.
Start the script as:
CC=xlc ./configure ...
DEC Ultrix:
Depending on the system configuration you might be unable to
compile 'interpret.c' with any kind of optimization at all.
The typical symptom is a failure during the compilation with
"Error code 1". When that happens, do the following steps:
- Make as usual until the "interpret" compilation
fails. The symptom is "Error code 1".
- Edit the makefile. Uncomment the compilation
line for debugging, and comment the compilation
line containing the optimization switches and
options.
- Command 'make interpret.o' .
- Edit the makefile again. Re-comment the line
for debugging, and uncomment the line with
the optimization switches and options.
- Start the make again. This time it will
complete as expected.
IPv6
----
If your machine supports IPv6, the driver can be configured to use it: give
'--enable-use-ipv6=yes' as argument to the configure script.
Beware: the driver support is rudimentary, experimental, and may cease to
function on the next full moon. Additional work is necessary to really
change _all_ address-related functions in driver and mudlibs.
IPv6 support was implemented by Uwe <mai94cch@studserv.uni-leipzig.de>.
In order to test IPv6 under Linux you need a 2.2.x kernel (preferably 2.2.10
or better) and the inet6 apps. If you use glibc 2.0, you need version 0.35
of the inet6 apps, or for glibc 2.1 you need version 0.36. The apps should
installed into /usr/inet6.
The apps are available on ftp.inner.net or on its mirrors, e.g.
<URL:ftp://6bone.informatik.uni-leipzig.de/pub/ftp.inner.net/pub/ipv6/inet6-apps-0.35.tar.gz>
resp.
<URL:ftp://6bone.informatik.uni-leipzig.de/pub/ftp.inner.net/pub/ipv6/inet6-apps0.36.tar.gz>
Furthermore you need a connection to the 6bone
(<URL:http://www.join.uni-muenster.de/>).
Newer versions of AIX also come with IPv6 support.
mySQL
-----
If your machine has mySQL installed, the driver can be configured to use
it: give '--enable-use-mysql=yes' as argument to the configure script.
Alternatively, if your mySQL uses an unusual include/library path,
the option can be given as '--enable-use-mysql=/unusual/path', which
will use the given path as search path for both include and library
files in addition to the normal system search paths. The include files will
be searched in <path>/include and <path>, the library files will be search
in <path>/lib/mysql, <path>/lib, and <path>, in this order.
The username and password for the mySQL database are specified by
the mudlib as arguments to the efun db_connect().
Use mysqladmin to create any databases you want to provide - the
names are later used in the efun db_connect() to connect to
the databases.
BeOS
----
Under BeOS, the driver can be configured and compiled in two ways:
1. Using the provided configuration files.
Copy the files machine.h, config.h and Makefile from src/hosts/be/
to src/. You may have to edit config.h and Makefile if you have
to use a specific mudlib with exotic settings.
Advantage of this method is that the Makefile allows cross-compilation
and covers Be specifics like file icon, type and version.
2. Use the Unix configure mechanism.
In general just follow the instructions given for Unix systems, but
be aware of the follwing:
- If the configure script doesn't properly work, try starting it
with
CC="$BE_C_COMPILER" EXTRA_CFLAGS="$BE_DEFAULT_C_FLAGS" ./configure
or (especially on BeOS R4.5 on PPC) even
CC=cc EXTRA_CFLAGS="$BE_DEFAULT_C_FLAGS" ./configure
- the created executable will have neither icon nor type.
- cross compilation is not supported.
- the Makefile defaults to best optimization which needs a lot
of memory during compilation.
If on driver start you get an error about an unknown hostname or
seems to hang, you're missing an /etc/hosts file:
- In the network preferences, select a name for you machine,
e.g. 'bebox'. The domain name field can be blank.
- Copy the /boot/beos/etc/hosts-sample file to /boot/beos/etc/hosts,
and edit it to contain at least the following lines:
127.0.0.1 bebox.my-isp.com bebox
127.1 bebox.my-isp.com bebox
(Note: in the file, every line has to start with the IP address -
no leading spaces are allowed).
With the current networking implementation, the driver is unable
to start ERQs. In order to use ERQs, you have to start them manually.
Other than this, the installation steps are the same as for Unix.
Windows 95/98/NT
----------------
To compile the gamedriver for Windows, you need the 'Cygwin' package,
which is a port of gcc, bash, and other GNU/Unix programs. Once it
is installed and running, the procedure is the same as under Unix.
CygWin is available from <URL:http://www.cygwin.com/> or
<http://www.redhat.com/software/cygwin/>, and when installing make sure that
your installation includes gcc, bash, make, sed, awk, and bison.
One common pitfall is if the installation does not create the '/bin'
directory in which the CygWin executables are expected. A typical
error message would be 'make: /bin/sh not found'.
Solution 1: Create a directory 'bin' in your search path (e.g. c:\bin)
and copy all required executables (in this case 'sh.exe') into
that directory.
Solution 2 (this can be done in a script): cd into the directory
where the executables are stored, e.g.
/compiler/Cygnus/B19/H-i386-cygwin32/bin . In this directory, give
the command 'mount $PWD /bin'.
Solution 3: Use a newer version of Cygwin where this problem apparently
has been solved.
A driver compiled by David Gibbs (ishara@btinternet.com) is available
at <URL:http://www.einy.freeserve.co.uk/lpmud.index.html>.
The following comments were provided by Robert Nurrie(?) who had
an misconfigured Cygnus installation (Summer 2000):
"Make sure you've used the setup.exe you get when clicking the 'install
cygwin now' link in the upper right corner of their website, instead of
the setup.exe found on the ftp-site.
Oh, and when you're given the choice of default text file type you
should of course chose Dos, as that seems to be the key."
Brandon Mathis points out, "that to compile under Windows you have to
use 'tar -xvfz' and not WinZIP to extract the source".
OS/2
----
The OS/2 port uses the EMX (a gcc version) to compile the gamedriver.
To compile the driver, copy the machine.h and Makefile from the hosts/os2
directory into the top directory and make as under Unix. The Makefile
is configured to install the driver in /mud - edit the variable 'prefix'
if you want a different directory.
Important: the Makefile must be save using the Unix lineend convention,
otherwise the make program will stop with an error. In the EPM editor
this can be achieved using the 'save -u' command.
If you have the full GNU suite installed, you can also configure the
driver as under Unix. Following caveats regarding the configure script:
- the ac_exeext variable may not be set to '.exe' as it should be;
- the calls to the program conftest may lack the ${ac_exeext} extension.
Additionally, configure does not properly recognize the settings for
HAVE_GETRUSAGE, GETRUSAGE_RESTRICTED, GETRUSAGE_VIA_SYSCALL and RUSAGE_USEC.
Copy these values from the provided machine.h
To recreate the configure script from configure.in, you need autoconf
version 2.50 or newer.

27
README Normal file
View file

@ -0,0 +1,27 @@
This is 'LDMud', a continuation of the Amylaar LPMud Gamedriver 3.1.2.
The latest stable version is available on
<ftp://ftp.mud.de/pub/software/gamedriver/ldmud/>,
the homepage is currently <http://www.bearnip.com/lars/proj/ldmud.html>.
The files in this directory are:
README : this file.
COPYRIGHT : the copyright of the driver.
CREDITS : the list of contributors.
HISTORY : a user-oriented history of the last changes.
CHANGELOG : the last changes in minute detail.
COPYING.LIB : the GNU Library Public License.
INSTALL : installation hints and notes.
doc/ : the collected documentation
src/ : the driver source
pkg/ : additional packages (unsupported and supported) for the driver.
mudlib/ : various LPC (example) files.
mudlib/sys/:
standard include files to copy into your mudlib. Some of these are
also used by the src/ files.
mud/ : the lp-245 mudlib, and support files for other mudlibs.

67
doc/3.2vs3.2.1 Normal file
View file

@ -0,0 +1,67 @@
NAME
3.2vs3.2.1
LAST UPDATE
Mon Apr 08 21:35:00 MET 1996
AUTHOR
Lars Duening, collecting from various snippets and fragments.
DESCRIPTION
There exists no real '3.2 vs 3.2.1' doc and the paragraphs in
the DONE file says only half the truth (it seems that Amylaar
still does not believe in docs besides those ending on .[chly]
:-)
Miscellaneous features:
- Variables are swapped.
- Telnet negotiation is done by the mudlib.
- IP name lookup is done by an external 'erq demon' which is
also able to perform other tasks.
- New predefined #defines:
__DOMAIN_NAME__, __HOST_IP_NUMBER__, __HOST_NAME__,
__MAX_RECURSION__, NO_NATIVE_MODE, __EFUN_DEFINED__()
- The master object has new/modified functions.
Quite a lot of functionality moved from the driver into the
mudlib by the use of driver hooks:
- move_object() and with it the call of init() and exit().
- Object initialisation (create() resp. reset()).
- Part of the command parsing.
- Nearly all native/compat mode differences.
- Uid handling.
New efuns:
attach_erq_demon(), binary_message(), debug_message(),
efun308(), expand_define(), extern_call(),
set_connection_charset(), get_type_info()
last_instructions(), query_shadowing(), regreplace(),
remove_action(),
send_erq(), set_buffer_size(), set_connection_charset(),
set_driver_hook(), set_this_player(), symbol_variable(),
to_int(), to_object(),
Modified efuns:
add_action(), creator(), export_uid(),
filter_array(), find_call_out(), functionlist(),
geteuid(), input_to(), move_object(),
process_string(), query_ip_number(), remove_call_out(),
set_modify_command(), seteuid(), shadow(),
snoop()
Removed efuns:
cindent(), find_living(), find_player(),
query_host_name(), set_living_name(), shout(),
wiz_list().
Modified Lfuns:
create(), init(), modify_command(),
reset(), query_real_name()
BUGS
Surely some.
SEE ALSO
native(C), applied(A), efun(E).

35
doc/FAQ Normal file
View file

@ -0,0 +1,35 @@
Q: When configuring or compiling the driver with the Cygwin package under
Windows, I get an error message like
configure: /bin/sh: No such file or directory
A: The installation of the Cygwin package is incomplete. Create a directory
C:\bin and copy the file 'sh.exe' into it.
Q: Since our mud runs on linux, the title screen is sometimes not appearing.
A: This seems to be some bug in the linux networking. You can circumvent it
by setting the buffer size to a very high value, like 32 KB . You can do
this with the efun set_buffer_size() .
Q: mudlibs?
A: ftp://ftp.cs.tu-berlin.de/pub/games/mud/lp/ftp/tub-mudlib.*.tar.gz
ftp://heute.cs.tu-berlin.de 7681 tub-mudlib.*.tar.gz
ftp://quest.tat.physik.uni-tuebingen.de/MUD/backups/
ftp://mud.uni-muenster.de/Software/MudLib/ (German)
ftp://ftp.UNItopia.de/pub/mudlib.tar.gz
resp. ftp://ftp.uni-stuttgart.de/pub/misc/games/mud/UNItopia/mudlib.tar.gz
ftp://ftp.tu-bs.de/pub/games/lpmud/2.4.5.tar.z
The ViKaR mudlib (3.2.1 compat) is available upon request, ask
vikar@unix-ag.uni-kl.de .
Offical releases will be made available via ftp on vikar.unix-ag.uni-kl.de
8888. WWW homepage : http://www.unix-ag.uni-kl.de/~europa/vikar
Q: Add-on packages?
A: ftp://heute.cs.tu-berlin.de 7681 intermud*.tar.gz
ftp://ftp.tu-bs.de/pub/games/lpmud/intermud-0.7beta.tar.gz
ftp://heute.cs.tu-berlin.de 7681 ftpd*.tar.gz
ftp://ftp.tu-bs.de/pub/games/lpmud/naseftpd-1.6.tar.gz

50
doc/LPC/alists Normal file
View file

@ -0,0 +1,50 @@
CONCEPT
alists
LAST UPDATE
2 Mar 92 21:10:21 GMT
AUTHOR
From: amylaar@mcshh.hanse.de (Joern Rennecke)
Subject: general documentation on alists
DESCRIPTION
Alists provide a fast and convenient way to access data
associatively.
Alists are implemented as arrays of arrays, the first being
the array holding the keys, the others arrays holding
associated data. An empty alist is an array of empty arrays.
Note that the the dimensions of the arrays are used the other
way than in lisp to allow for faster searching.
Keys have to be of type integer, string or object. Types can
be mixed.
The search functions return an undefined value when another
list is given in place of a presorted key list.
A list with non-numeric keys retrieved by restore_object() has
to be readjusted by using order_alist(), especially after
reboot.
Deleting an entry can safely be done with exclude_array as
long as all associated data lists are treated like the key
array; index finding for such purposes can be done with assoc.
Typical applications: holding administrary information about
wizards, list of visitors in a pub, list of customers having
some sort of credit, information remembered about items etc.
NOTE
The main use of alists, storing data associatively, is now
better performed by mappings. Alists are needed for more
extreme situations only.
CAVEATS
A maximum array size of 1000 might prove to be to small.
SEE ALSO
mappings(LPC), order_alist(E), insert_alist(E), assoc(E),
transpose_array(E)

116
doc/LPC/arrays Normal file
View file

@ -0,0 +1,116 @@
CONCEPT
arrays
DESCRIPTION
There is support for arrays. The arrays can't be declared, but
should be allocated dynamically with the function 'allocate()'
(see efun/allocate).
Arrays are stored by reference, so all assignments of whole
arrays will just copy the address. The array will be
deallocated when no variable points to it any longer.
When a variable points to an array, items can be accessed with
indexing: 'arr[3]' as an example. The name of the array being
indexed can be any expression, even a function call:
'func()[2]'. It can also be another array, if this array has
pointers to arrays:
arr = allocate(2);
arr[0] = allocate(3);
arr[1] = allocate(3);
Now 'arr[1][2]' is a valid value.
The 'sizeof()' function (in true C a compiler-directive, not a
function) will give the number of elements in an array (see
efun/sizeof).
NOTE
Nowadays it is most of the time preferable to use an array
constructor, a list surrounded by '({' and '})',
e.g. ({ 1, "xx", 2 }) will construct a new array with size 3,
initialized with 1, "xx" and 2 respectively.
OPERATIONS
INDEXING
There are several very useful operations defined on arrays.
The most used is the indexing:
a=({ 0,1,2,3 });
return a[2]; // this will return 2
You also can count from the end of the array. Use <1 to specify
the last element in the array:
a=({ 0,1,2,3 });
return a[<3]; // this will return 1
With indexing you can also create sub-arrays:
a=({ 0,1,2,3,4,5,6,7 });
return a[3..5]; // this will return ({ 3,4,5 })
return a[2..<2]; // this will return ({ 2,3,4,5,6 })
return a[<5..<3]; // this will return ({ 3,4,5 })
return a[<6..5]; // this will return ({ 2,3,4,5 })
return a[3..3]; // this will return ({ 3 })
return a[3..2]; // this will return ({ })
return a[3..0]; // this will return ({ })
return a[5..100]; // this will return ({ 5,6,7 })
[x..] is interpreted as [x..<1]
ADDING
You can add two arrays. The result is one array with the elements
of both the former arrays:
a=({ 0,1 });
b=({ "a","b" });
return a+b; // this will return ({ 0,1,"a","b" })
return b+a; // this will return ({ "a","b",0,1 })
SUBTRACTING
You can erase all elements of one array that occur in another
array:
a=({ 0,1,2,3,4,5,6,7 });
b=({ 7,2,5,8,1,9 });
return a-b; // this will return ({ 0,3,4,6 })
return b-a; // this will return ({ 8,9 })
INTERJUNCTION
Use the &-operator to create the interjunction of two arrays:
a=({ 5,2,8,1,9,4 })
b=({ 1,6,7,3,4,5 })
return a&b; // this will return ({ 1,4,5 })
ASSIGNING
Assigning can also be done to sub-arrays and is thus very powerful:
a=({ 0,1,2,3,4,5,6,7 });
a[<4..<3]=({ 8,9 });
return a; // this will return ({ 0,1,2,3,8,9,6,7 })
a=({ 0,1,2,3,4,5,6,7 });
a[2..5]=({ });
return a; // this will return ({ 0,1,6,7 })
a=({ 0,1,2,3,4 });
a[3..2]=({ 8,9 });
return a; // this will return ({ 0,1,2,8,9,3,4 })
a=({ 0,1,2,3,4 });
a[3..0]=({ 8,9 });
return a; // this will return ({ 0,1,2,8,9,1,2,3,4 })
// this is quite funny but true ;-)
// WARNING: If done unintentionally and
// within a loop, you can quickly cause
// the game to run out of memory!
GENERAL
Of course for any of the operators explained above you can use
the combined form of assigning and operating; that means the
operators +=, -= and &= work.
TIPS
If you want to make sure that no element is more than once in an
array you can use the following:
a = m_indices(mkmapping(a));
This creates a mapping out of the array and recreates the array
at once. The elements in the array can be shuffled by this
procedure.

46
doc/LPC/block Normal file
View file

@ -0,0 +1,46 @@
NAME
block
DESCRIPTION
A block is a special statment, that begins with '{', contains
a list of statements, and ends with '}'.
The block may define local variables. If for a variable no
initialisation is given, the variable is initialised to 0 every
time the block is entered. Otherwise, the initialisation
expression is evaluated and its result assigned to the variable
everytime the block is entered.
Example definitions are:
int i;
int j = 3;
int k = 3 * j, l;
Here, i and l are both initialised to 0; j is initialised
to 3, and k is initialised to 9 (3 * j).
Local variables defined in a block are visible only until the
end of the block. Definitions in an inner block hide definitions in
outer blocks.
HISTORY
Up to 3.2.7, local variables were visible (from their point of
definition) in the whole function. That is, code like
do {
int res;
res = ...
} while (res == 5);
write(res);
was perfectly legal. It is no longer, as 'res' ceases to exist
with the closing '}' of the while().
You can get this old behaviour back with the #pragma no_local_scopes.
To turn it off again, use #pragma local_scopes.
Up to 3.2.8, local variables could not be initialised in their
definition.

730
doc/LPC/closure_guide Normal file
View file

@ -0,0 +1,730 @@
Closure Guide for LPC
Table of Contents
1 Indroduction, Overview and Efun-Closures
2 Lfun-, Inline and Lambda-Closures
2.1 Lfun-Closures
2.2 Inline-Closures
2.3 Lambda-Closures
2.3.1 Advantages of Lambda-Closures
2.3.2 Free Variables in Lambda-Closure Constructs
2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
2.4 Closures with Strange Names
2.5 Operator-Closures
2.6 Variable-Closures
3 Examples
3.1 Lfun-Closure
3.2 Lambda-Closure
1 Introduction, Overview and Efun-Closures
A closure is a pointer to a function. That means that it is data like an
int or a string are. It may be assigned to a variable or given to anoth-
er function as argument.
To create a closure that points to an efun like write() you can write
the name of the efun prepended with "hash-tick": #'. #'write is a clo-
sure that points to the efun write().
I very often put parentheses around such a closure-notation because
otherwise my editor gets confused by the hashmark: (#'write). This is
especially of interest within lambda-closures (see below).
A closure can be evaluated (which means that the function it points to
is called) using the efuns funcall() or apply(), which also allow to
give arguments to the function. Example:
funcall(#'write,"hello");
This will result in the same as write("hello"); alone. The string
"hello" is given as first (and only) argument to the function the clo-
sure #'write points to.
The return value of the function the closure points to is returned by
the efun funcall() or apply(). (Since write() always returns 0 the re-
turn value of the example above will be 0.)
What are closures good for? With closures you can make much more univer-
sally usable functions. A good example is the function filter_array().
It gets an array and a closure as arguments. Then it calls the function
the closure points to for each element of the array:
filter_array(({ "bla","foo","bar" }),#'write);
This will call write("bla"), write("foo") and write("bar") in any order;
the order is undefined.
(In the current implementation the given closure is evaluated for all
elements from the first to the last, so the output will be "blafoobar".)
Furthermore the efun filter_array() examines the return value of each
call of the function the closure points to (the return value of the
write()s). If the value is true (not 0) then this element is put into
another array which filter_array() builds up. If the return value is
false (== 0) then this element is _not_ put into this array. When all
calls are done the slowly built up array is returned. Thus,
filter_array() filters from the given array all elements that the given
closure evaluates "true" for and returns an array of those. (The array
given to filter_array() itself is _not_ changed!)
A senseful example for filter_array would be this:
x = filter_array(users(),#'query_is_wizard);
users() is an efun that gets no arguments and returns an array of all
logged in players (wizards and players). query_is_wizard() is a
simul_efun that gets an object as first (and only) argument and returns
true (1) if this object is a wizard and 0 otherwise.
So, for each element of the array returned by users() the function
query_is_wizard() is called and only those for which 1 was returned are
collected into the result and then put into the variable x.
We now have all logged in wizards stored as array in the variable x.
Another example: We want to filter out all numbers that are greater than
42 from the array a of integers:
x = filter_array(({ 10,50,30,70 }),#'>,42);
(x will now be ({ 50,70 }).)
Here two things are new: first: we create a closure that points to an
operator; second: we use the possibility to give extra arguments to
filter_array().
Like all efuns the usual operators can be pointed to with a closure by
prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5).
The extra arguments given as third to last argument (as many as you
like) to filter_array() are given as second to last argument to the
function pointed to by the closure each time it is called.
Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ...
(which is (10>42), (50>42) ...) and return an array of all elements this
returns true for and store it into x.
If you want to create a closure to an efun of which you have the name
stored in a string you can create such an efun-closure with the efun
symbol_function():
symbol_function("write") // this will return #'write
funcall(symbol_function("write"),"foobar"); // == write("foobar");
This function does not very often occur in normal code but it is very
useful for tool-programming (eg the robe uses symbol_function() to allow
you call any efun you give).
2 Lfun- and Lambda-Closures
Very often the possibilities closures to efuns offer are not sufficient
for the purpose one has. In nearly all cases three possibilities exist in
such cases: use an lfun- or inline-closure, or a lambda-closure.
2.1 Lfun-Closures
The first possibility is rather easy: like with the efun-closures you
can create a pointer to a function in the same object you are by using
the #' to prepend it to a function name of a function declared above.
Example:
status foo(int x) {
return ((x*2) > 42);
}
int *bar() {
return filter_array(({ 10,50,30,70 }),#'foo);
}
Thus, #'foo is used like there was an efun of this name and doing the
job that is done in foo().
2.2 Inline Closure
Inline closures are a variant of lfun closures, the difference being
that the function text is written right where the closure is used,
enclosed in a pair of '(:' and ':)'. The compiler will then take care
of creating a proper lfun and lfun-closure. The arguments passed to
such an inline closure are accessible by position: $1 would be the
first argument, $2 the second, and so on. With this, the
above example would read:
int * bar() {
return filter_array(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
}
or alternatively:
int * bar() {
return filter_array(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
}
The difference between the two versions is that in the first form the text
of the inline closure must be an expression only, whereas in the second
form any legal statement is allowed. The compiler distinguishes the two
forms by the last character before the ':)': if it's a ';' or '}', the
compiler treats the closure as statement(s), otherwise as expression.
Inline closures may also nested, so that the following (not very useful)
example is legal, too:
return filter_array( ({ 10, 50, 30, 70 })
, (: string *s;
s = map_array(users(), (: $1->query_name() :));
return s[random(sizeof(s))] + ($1 * 2);
:));
The notation of inline closures is modelled after the MudOS functionals,
but there are a few important differences in behaviour.
2.3 Lambda-Closures
Lambda-Closures take the idea of 'define it where you use it' one step
further. On first glance they may look like inline closures with an uglier
notation, but they offer a few increased possibilities. But first things
first.
The efun lambda() creates a function temporarily and returns a closure
pointing to this function. lambda() therefor gets two arrays as
arguments, the first is a list of all arguments the function shall expect
and the second array is the code of the function (in a more or less
complicated form; at least not in C- or LPC-syntax). The closure #'foo
from the example above could be notated as lambda-closure:
lambda(({ 'x }),({ (#'>),
({ (#'*),'x,2 }),
42
}))
Now, the first argument is ({ 'x }), an array of all arguments the
function shall expect: 1 argument (called 'x) is expected. Notice the
strange notation for this argument with one single leading tick. Like
The hash-tick to denote closures the leading tick is used to denote
things called "symbols". They do not differ much from strings and if
you do not want to have a deeper look into closures you can leave it
this way.
The second argument is an array. The first element of such an array
must be an efun- or an lfun-closure, the further elements are the
arguments for the function this closure points to. If such an argu-
ment is an array, it is treated alike; the first element must be a
closure and the remaining elements are arguments (which of course
also might be arrays ...).
This leads to a problem: sometimes you want to give an array as an
argument to a function. But arrays in an array given to lambda() are
interpreted as code-arrays. To allow you to give an array as an argu-
ment within an array given to lambda(), you can use the function
quote() to make your array to a quoted array (a quoted array is for
an array what a symbol is for a string):
lambda(0,({ (#'sizeof),
quote(({ 10,50,30,70 }))
}))
For array constants, you can also use a single quote to the same
effect:
lambda(0,({ (#'sizeof),
'({ 10,50,30,70 })
}))
This lambda-closure points to a function that will return 4 (it will
call sizeof() for the array ({ 10,50,30,70 })). Another thing: if
we want to create a function that expects no arguments, we can give
an empty array as first argument to lambda() but we can give 0 as
well to attain this. This is just an abbreviation.
Lambda-closure constructs can become quite large and hard to read. The
larger they become the harder the code is to read and you should avoid
extreme cases. Very often the possibility to use an lfun or an inline
instead of a large lambda shortens the code dramatically. Example:
status foo(object o) {
return environment(o)->query_level()>WL_APPRENTICE;
}
x=filter_array(a,#'foo);
does the same as
x=filter_array(a,lambda(({ 'o }),
({ (#'>),
({ (#'call_other),
({ (#'environment),'o }),
"query_level"
}),
WL_APPRENTICE
})));
(Note that the syntax with the arrow "->" for call_other()s cannot be
used, #'-> does not exist. You have to use #'call_other for this and
give the name of the lfun to be called as a string.)
This example also demonstrates the two disadvantages of lambda closures.
First, they are very difficult to read, even for a simple example like
this. Second, the lambda closure is re-created everytime the
filter_array() is executed, even though the created code is always the
same.
'Why use lambdas at all then?' you may ask now. Well, read on.
2.3.1 Advantages of Lambda Closures
The advantages of lambdas stem from the fact that they are created
at runtime from normal arrays.
This means that the behaviour of a lambda can be made dependant on data
available only at runtime. For example:
closure c;
c = lambda(0, ({#'-, ({ #'time }), time() }) );
Whenever you now call this closure ('funcall(c)') it will return the
elapsed time since the closure was created.
The second advantage of lambdas is that the arrays from which they
are compiled can be constructed at runtime. Imagine a customizable prompt
which can be configured to display the time, the environment, or both:
mixed code;
code = ({ "> " });
if (user_wants_time)
code = ({ #'+, ({ #'ctime }), code });
if (user_wants_environment)
code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) })
, code });
set_prompt(lambda(0, code));
2.3.2 Free Variables in Lambda-Closure Constructs
You can use local variables in lambda constructs without declaring
them, just use them. The only limitation is that you at first have
to assign something to them. Give them as symbols like you do with
the arguments. This feature does not make much sense without the use
of complexer flow controlling features described below.
The closure #'= is used to assign a value to something (like the
LPC-operator = is).
2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
There are some special closures that are supposed to be used only
within a lambda construct. With them you can create nearly all code
you can with regular LPC-code like loops and conditions.
#'? acts like the "if" statement in LPC. The first argument is the
condition, the second is the code to be executed if the condition
returns true. The following arguments can also be such couples of
code-arrays that state a condition and a possible result. If at
the end there is a single argument, it is used as the else-case
if no condition returned true.
lambda(({ 'x }),({ (#'?), // if
({ (#'>),'x,5 }), // (x > 5)
({ (#'*),'x,2 }), // result is x * 2;
({ (#'<),'x,-5 }), // else if (x < -5)
({ (#'/),'x,2 }), // result is x/2;
'x // else result is x;
}))
#'?! is like the #'? but it negates all conditions after evaluation
and thus is like an ifnot in LPC (if there were one).
#', (which looks a bit strange) is the equivalent of the comma-operator
in LPC and says: evaluate all arguments and return the value of
the last. It is used to do several things inside a lambda-closure.
lambda(({ 'x }),({ (#',), // two commas necessary!
// one for the closure and one as
// delimiter in the array
({ (#'write),"hello world!" }),
({ (#'say),"Foobar." })
}))
#'while acts like the LPC statement "while" and repeats executing one
code-array while another returns true.
#'while expects two or more arguments: the condition as first
argument, then the result the whole expression shall have after
the condition turns false (this is in many cases of no interest)
and as third to last argument the body of the loop.
lambda(0,({ (#',), // several things to do ...
({ (#'=),'i,0 }), // i is a local variable of this
// lambda-closure and is
// initialized with 0 now.
({ (#'while),
({ (#'<),'i,10 }), // condition: i < 10
42, // result is not interesting,
// but we must give one
({ (#'write),'i }), // give out i
({ (#'+=),'i,1 }) // increase i
})
}))
The function this closure points to will give out the
numbers from 0 to 9 and then return 42.
#'do is like the do-while statement in LPC and is very much like the
#'while. The difference is that #'while tests the condition al-
ready before the body is evaluated for the first time, this means
that the body might not be evaluated even once. #'do evaluates
the body first and then the condition, thus the body is evaluated
at least one time.
Furthermore, the arguments for #'do are changed in order. #'do
expects as first to last but two the body of the loop, then the
condition (as last-but-one'th argument) and the result value as
last argument. So #'do must have at least two arguments: the
condition and the result.
lambda(0,({ (#',), // several things to do ...
({ (#'=),'i,0 }), // i is a local variable of this
// lambda-closure and is initialized
// with 0 now.
({ (#'do),
({ (#'write),'i }), // give out i
({ (#'+=),'i,1 }) // increase i
({ (#'<),'i,10 }), // condition: i < 10
42 // result is not interesting
})
}))
NOTE: There is no #'for in LPC, you should use #'while for this.
#'foreach is like the foreach() statement in LPC. It evaluates one or
more bodies repeatedly for every value in a giving string, array
or mapping. The result of the closure is 0.
#'foreach expects two or more arguments:
- a single variable symbol, or an array with several variable
symbols
- the value to iterate over
- zero or more bodes to evaluate in each iteration.
The single values retrieved from the given value are assigned
one after another to the variable(s), then the bodies are executed
for each assignment.
lambda(0, ({#'foreach, 'o, ({#'users})
, ({#'call_other, 'o, "die" })
}));
lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...})
, ({#'printf, "%O:%O\n", 'k, 'v })
}));
#'return gets one argument and acts like the "return" statement in LPC
in the function that is created by lambda(). It aborts the
execution of this function and returns the argument.
lambda(0,({ (#'while),// loop
1, // condition is 1 ==> endles loop
42, // return value (which will never be used)
({ (#'write),"grin" })
({ (#'?!), // ifnot
({ (#'random),10 }), // (random(10))
({ (#'return),100 }) // return 100;
})
}))
This function will enter an endles loop that will in each
turn give out "grin" and if random(10) returns 0 (which will
of course happen very soon) it will leave the function with
"return 100". The value 42 that is given as result of the
loop would be returned if the condition would evaluate to 0
which cannot be. (1 is never 0 ;-)
#'break is used like the "break" statement in LPC and aborts the exe-
cution of loops and switches.
It must not appear outside a loop or switch of the lambda
closure itself, it cannot abort the execution of the function
the closure points to!
lambda(0,({ (#'?),
({ (#'random),2 }),
({ (#'break) }), // this will cause the error
// "Unimplemented operator break
// for lambda()"
"random was false!"
}));
You can use ({ #'return,0 }) instead of ({ #'break }) in such
cases.
#'continue is used like the "continue" statement in LPC and jumps to
the end of the current loop and continues with the loop
condition.
#'default may be used within a #'switch-construct but be careful!
To call symbol_function("default") (which is done usually
by tools that allow closure-creation) might crash the
driver! So please do only use it within your LPC-files.
(NOTE: This driver bug is fixed somewhere below 3.2.1@131.)
#'.. may be used within a #'switch-construct but is not implemented
yet (3.2.1@131). But #'[..] works well instead of it.
#'switch is used to create closures which behave very much like the
switch-construct in LPC. To understand the following you
should already know the syntax and possibilities of the
latter one (which is mightier than the C-version of switch).
I will confront some LPC versions and the corresponding clo-
sure versions below.
LPC: Closure:
switch (x) { lambda(0,({ (#'switch), x,
case 5: ({ 5 }),
return "five"; ({ (#'return),"five" }),
(#',),
case 6..9: ({ 6, (#'[..]), 9 }),
return "six to nine"; ({ (#'return),
"six to nine" }),
(#',),
case 1: ({ 1 }),
write("one"); ({ (#'write),"one" }),
// fall through (#',),
case 2: ({ 2,
case 10: 10 }),
return "two or ten"; ({ (#'return),
"two or ten" }),
(#',),
case 3..4: ({ 3, (#'[..]), 4 }),
write("three to four"); ({ (#'write),
"three to four" }),
break; // leave switch (#'break),
default: ({ (#'default) }),
write("something else"); ({ (#'write),
"something else" }),
break; (#'break)
} }))
#'&& evaluates the arguments from the first on and stops if one evalu-
ates to 0 and returns 0. If none evaluates to 0 it returns the
result of the last argument.
#'|| evaluates the arguments from the first on and stops if one evalu-
ates to true (not 0) and returns it. If all evaluate to 0 it
returns 0.
#'catch executes the closure given as argument, but catches any
runtime error (see catch(E)). Optionally the symbols 'nolog
and 'publish may be given as additional arguments to modify
the behaviour of the catch.
#'sscanf acts similar to how a funcall would, but passes the third
and following arguments as lvalues, that is, values which can
be assigned to.
#'= and the #'<op>= variants are also special because the first
argument has to be an lvalue.
2.4 Closures with Strange Names
#'negate is the unary minus that returns -x for the argument x.
map_array(({ 1,2,3 }),#'negate)
This returns ({ -1,-2,-3 }).
#'[ is used for the things that in LPC are done with
the []-operator (it indexes an array or a mapping).
lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30
lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50
#'[< is the same as #'[ but counts the elements from the
end (like indexing with [] and the "<").
#'[..] returns a subarray of the argument from the one
given index to the other given index, both counted from the
beginning of the array.
#'[..<]
#'[<..]
#'[<..<] same as above, but the indexes are counted from the end,
lambda(0,({ #'[..<],
quote(({ 0,1,2,3,4,5,6,7 })),2,3
}))
This will return ({ 2,3,4,5 }).
#'[..
#'[<.. same as above, but only the first index is given, the
subarray will go till the end of the original (like with
[x..]).
#'({ is used to create arrays (as with ({ }) in LPC). All arguments
become the elements of the array.
lambda(0,({ #'({,
({ (#'random),10 }),
({ (#'random),50 }),
({ (#'random),30 }),
({ (#'random),70 })
}))
This returns ({ random(10),random(50),random(30),random(70) }).
#'([ is used to create mappings out of single entries (with seve-
ral values) like the ([ ]) in LPC. Very unusual is the fact
that this closure gets arrays as argument that are not eval-
uated, although they are not quoted.
lambda(0,({ #'([,
({ "x",1,2,3 }),
({ "y",4,5,6 })
}));
This returns ([ "x": 1;2;3,
"y": 4;5;6 ]).
However, the elements of the arrays are evaluated as lambda
expressions, so if you want to create a mapping from values
evaluated at call time, write them as lambda closures:
lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) )
will return ([ 1: <result of ctime() at call time ]).
Arrays can be put into the mapping by quoting:
lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) )
will return ([ 1: ({ 2 }) ])
#'[,] is nearly the same as #'[. The only difference
shows up if you want to index a mapping with a width
greater than 1 (with more than just one value per
key) directly with funcall(). Example:
funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1)
This will not work. Use #'[,] and it will
work. If you want to use it in a lambda closure you
do not have to use #'[,] and #'[ will
do fine. On the other hand, #'[,] cannot
work with arrays, so in nearly all cases use #'[
and just in the described special case, use
#'[,].
This is a strange thing and I deem it a bug, so it
might change in the future.
2.5 Operator-Closures
Most of the closures that are used for things which are done by opera-
tors are in fact not operator-closures but efun-closures. But there are
a few which do not have the state of efun-closures but are called
"operator-closures". #'return is an example, a complete list of them is
given below.
These closures cannot be called directly using funcall() or apply() (or
other efuns like filter_array()), but must appear only in lambda-con-
structs.
funcall(#'return,4); // does not work! This will raise an
// Uncallable-closure error.
funcall(lambda(0, // this is a correct example
({ (#'return),4 })
));
All operator-closures:
#'&&
#'||
#',
#'?
#'?!
#'=
#'<op>=
#'++
#'--
#'break
#'catch
#'continue
#'default
#'do
#'foreach
#'return
#'sscanf
#'switch
#'while
#'({
#'([
#'.. is very likely to be an operator closure too, but since it is
not implemented yet, I cannot say for sure.
2.6 Variable-Closures
All object-global variables might be "closured" by prepending a #' to
them to allow access and/or manipulation of them. So if your object has
a global variable x you can use #'x within a closure.
Normally you will treat those expressions like lfun-closures: put them
into an array to get the value:
object.c:
int x;
int foo() {
return lambda(0,({ (#'write),({ (#'x) }) }));
}
Anybody who now calls object->foo() will get a closure which will, when
evaluated, write the actual value of object's global variable x.
Variable closures do not accept arguments.
3 Examples
In this section I will give and explain some examples coming out of
praxis. If the explanation seems to be in some cases too detailed this
can be explained by the trial to allow the reader to read the examples
section first ;-)
3.1 Lfun-Closure
An item with a complex long-description like a watch that shall always
show the actual time will usually base upon the complex/item-class and
give an lfun-closure as argument to the set_long()-method.
watch.c:
inherit "complex/item";
string my_long() {
return ("The watch is small and has a strange otherworldly"
" aura about it.\n"
"The current time is: "+ctime()+".\n");
}
void create() {
set_short("a little watch");
set_id(({ "watch","little watch" }));
set_long(#'my_long); // the lfun-closure to the lfun my_long()
}
3.2 Lambda-Closure
The example from 3.1 can also be written using a lambda-closure.
watch.c:
inherit "complex/item";
void create() {
set_short("a little watch");
set_id(({ "watch","little watch" }));
set_long(lambda(0,({ (#'+),
"The watch is small and has a strange"
" otherworldly aura about it.\n"
"The current time is: ",
({ (#'+),
({ (#'ctime) }),
".\n"
})
})));
}

506
doc/LPC/closures Normal file
View file

@ -0,0 +1,506 @@
CONCEPT
closures
NOTE
This is the official man page concerning closures. If you find
it hard to read maybe the Closure Guide is an easier introduction
for you: See closure_guide(LPC) and closures-example(LPC).
DESCRIPTION
Closures provide a means of creating code dynamically and
passing pieces of code as parameters, storing them in
variables. One might think of them as a very advanced form of
process_string(). However, this falls short of what you can
actually do with them.
The simplest kind of closures are efuns, lfuns or operators.
For example, #'this_player is an example of a closure. You can
assign it to a variable as in
closure f;
object p;
f = #'this_player;
and later use either the funcall() or apply() efun to evaluate
it. Like
p = funcall(f);
or
p = apply(f);
In both cases there p will afterwards hold the value of
this_player(). Of course, this is only a rather simple
application.
Inline closures are a variant of lfun closures, the difference being
that the function text is written right where the closure is used,
enclosed in a pair of '(:' and ':)'. The compiler will then take care
of creating a proper lfun and lfun-closure. The arguments passed to
such an inline closure are accessible by position: $1 would be the
first argument, $2 the second, and so on. With this, the above example
would read:
int * bar() {
return filter_array(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
}
or alternatively:
int * bar() {
return filter_array(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
}
The difference between the two versions is that in the first form the
text of the inline closure must be an expression only, whereas in the
second form any legal statement is allowed. The compiler distinguishes
the two forms by the last character before the ':)': if it's a ';' or
'}', the compiler treats the closure as statement(s), otherwise as
expression.
Inline closures may also nested, so that the following (not very
useful) example is legal, too:
return filter_array( ({ 10, 50, 30, 70 })
, (: string *s;
s = map_array(users(), (: $1->query_name() :));
return s[random(sizeof(s))] + ($1 * 2);
:));
The notation of inline closures is modelled after the MudOS
functionals, but there are a few important differences in behaviour.
More useful instances of closures can be created
using the lambda() efun. It is much like the lambda function
in LISP. For example, you can do the following:
f = lambda( ({ 'x }), ({ #'environment, 'x }) );
This will create a lambda closure and assign it to f. The
first argument to lambda is an array describing the arguments
(symbols) passed to the closure upon evaluation by funcall()
or apply(). You can now evaluate f, for example by means of
funcall(f,this_object()). This will result in the following
steps:
1. The value of this_object() will be bound to symbol x.
2. environment(x) evaluates to environment(this_object())
and is returned as the result of the funcall().
One might wonder why there are two functions, funcall() and
apply(), to perform the seemingly same job, namely evaluating
a closure. Of course there is a subtle difference. If the last
argument to apply() is an array, then each of its elements
gets expanded to an additional paramater. The obvious use
would be #'call_other as in:
mixed eval(object ob,string func,mixed *args) {
return apply(#'call_other,ob,func,args);
}
This will result in calling
ob->func(args[0],args[1],...,args[sizeof(args)-1]). Using
funcall() instead of apply() would have given us
ob->func(args).
Of course, besides efuns there are closures for operators,
like #'+, '-, #'<, #'&&, etc.
Well, so far closures have been pretty much limited despite
their obvious flexibility. This changes now with the
introduction of conditional and loop operators. For example,
try:
closure max;
max = lambda( ({ 'x, 'y }),
({ #'? ,({ #'>, 'x, 'y }), 'x, 'y }) );
return funcall(max,7,3);
The above example will return 7. What happened? Of course #'?
is the conditional operator and its 'syntax' is as follows:
({ #'?, cond1, val1, cond2, val2, ..., condn, valn,
valdefault });
It evaluates cond1, cond2, ..., condn successively until it
gets a nonzero result and then returns the corresponding
value. If there is no condition evaluating to a nonzero
result, valdefault gets returned. If valdefault is omitted, 0
gets returned. #'?! works just like #'?, except that the !
operator is applied to conditions before testing. Therefore,
while #'? is somewhat like an if statement, #'?! resembles an
if_not statement if there were one.
There are also loops:
({ #'do, loopbody1, ..., loopbodyN, loopcond, loopresult })
will evaluate the loopbodies until loopcond evaluates to 0 and
then return the value of loopresult. Symbols may be used as
variables, of course.
({ #'while, loopcond, loopresult, loopbody1, ..., loopbodyN })
works similar but evaluates loopcond before the loopbodies.
The foreach() loop also exists:
({ #'foreach, 'var, expr, loopbody1, ..., loopbodyN })
({ #'foreach, ({ 'var1, ..., 'varN}) , expr
, loopbody1, ..., loopbodyN })
Now on to a couple of tricky things:
a) How do I write down an array within a lambda closure to
avoid interpretation as a subclosure?
({ #'member_array, 'x, ({ "abc", "xyz" }) }) will obviously
result in an error as soon as lambda() tries to interpret
"abc" as a closure operator. The solution is to quote the
array, as in: ({ #'member_array, 'x, '({ "abc", "xyz" }) }).
Applying lambda() to this will not result in an error.
Instead, the quote will be stripped from the array and the
result regarded as a normal array literal. The same can be
achieved by using the efun quote(), e.g.:
({ #'member_array, 'x, quote( ({ "abc", "xyz" }) ) })
b) Isn't it a security risk to pass, say, a closure to the
master object which then evaluates it with all the
permissions it got?
Luckily, no. Each closure gets upon compilation bound to
the object defining it. That means that executing it first
sets this_object() to the object that defined it and then
evaluates the closure. This also allows us to call lfuns
which might otherwise be undefined in the calling object.
There is however, a variant of lambda(), called
unbound_lambda(), which works similar but does not allow
the use of lfuns and does not bind the closure to the
defining object. The drawback is that trying to evaluate it
by apply() or funcall() will result in an error. The
closure first needs to be bound by calling bind_lambda().
bind_lambda() normally takes one argument and transforms an
unbound closure into a closure bound to the object
executing the bind_lambda().
Privileged objects, like the master and the simul_efun
object (or those authorized by the privilege_violation()
function in the master) may also give an object as the
second argument to bind_lambda(). This will bind the
closure to that object. A sample application is:
dump_object(ob)
// will dump the variables of ob to /dump.o
{
closure save;
save = unbound_lambda( ({ }),
({ #'save_object, "/open/dump" }) );
bind_lambda(save,ob);
funcall(save);
}
bind_lambda() can also be used with efun closures.
c) It might be an interesting application to create closures
dynamically as an alternative to writing LPC code to a file
and then loading it. However, how do I avoid doing exactly
that if I need symbols like 'x or 'y?
To do that one uses the quote() efun. It takes a string as
its argument and transforms it into a symbol. For example,
writing quote("x") is exactly the same as writing 'x.
d) How do I test if a variable holds a closure?
Use the closurep() efun which works like all the other type
testing efuns. For symbols there is also symbolp()
available.
e) That means, I can do:
if (closurep(f)) return funcall(f); else return f; ?
Yes, but in the case of funcall() it is unnecessary. If
funcall() gets only one argument and it is not a closure it
will be returned unchanged. So return funcall(f); would
suffice.
f) I want to use a function in some object as a closure. How do I do
that?
There are several ways. If the function resides in
this_object(), just use #'func_name. If not, or if you want
to create the function dnynamically, use the efun
symbol_function(). It takes a string as it first and an
object as its second argument and returns a closure which
upon evaluation calls the given function in the given
object (and faster than call_other(), too, if done from
inside a loop, since function search will be done only when
calling symbol_function().
g) Can I create efun closures dynamically, too?
Yes, just use symbol_function() with a single argument.
Most useful for marker objects and the like. But
theoretically a security risk if not used properly and from
inside a security relevant object. Take care, however,
that, if there is a simul_efun with the same name, it will
be preferred as in the case of #'function. Use the efun::
modifier to get the efun if you need it.
h) Are there other uses of closures except using them to store
code?
Lots. For example, you can use them within almost all of
the efuns where you give a function as an argument, like
filter_array(), sort_array() or walk_mapping().
sort_array(array,#'>) does indeed what is expected. Another
application is set_prompt(), where a closure can output
your own prompt based on the current time and other stuff
which changes all the time.
Finally, there are some special efun/operator closures:
#'[ : indexes an array.
#'[< : does the same, but starting at the end.
#'[..] : gets an array and two numbers
and returns a sub-array.
#'[..<] : same as above but the second index is
interpreted as counted from the left end.
#'[<..] and
#'[<..<] : should be clear now.
#'[.. : takes only one index and returns the sub-
array from this index to the end.
#'[<.. : same as above but the index is interpreted
as counted from the left end.
#'({ : puts all arguments into an array.
#'([ : gets an unquoted (!) array which must include
at least one element as argument and returns a mapping of
the width of the given array's size with one entry that
contains the first element as key and the other elements
as values to the key.
#'negate is for unary minus.
#', may be followed by any number of closures,
e.g.: ({ (#',),
({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
will swap 'a and 'b when compiled and executed.
------------
An example from Amylaar:
I have tested the replace_program() functionality with one
example, which I include below. The room is commonly known as
/room/orc_valley.c . A prerequisite to make this work is to
have valued properties in room.c .
The property C_EXTRA_RESET, if defined, is evaluated at reset
time in the reset() of the used room.c . Moreover, you need
a function to query an unprocessed property to use
orc_valley.c from fortress.c (That is, don't do an automatic
funcall there.) If you can't supply such a function, you
have to set the property "get_orc" at the end of orc_valley.c
's extra_reset() with: add_prop("get_orc", lambda(0, get_orc)
); which will set the property to a function that returns the
function that is in the variable get_orc at the time you do
the add_prop() call.
Back to fortress.c : Assume you have the function
successfully queried and stored in the variable get_orc. Now
you can compute your extra_reset() function by: get_orc =
lambda( 0, ({#'funcall, get_orc, 8, 40}) ); which creates the
usual 8 orcs with an a_chat chance of 40.
Here comes the orc_valley.c source:
----------- cut here ------- cut here -------- cut here ---
#include "room.h"
#include "/sys/stdproperties.h"
#undef EXTRA_RESET
#define EXTRA_RESET extra_reset();
extra_reset() {
closure get_orc;
replace_program("room/room"); /* Must come first. */
get_orc = lambda( ({'num_orcs, 'chat_chance}),
({#'?!, ({#'present, "orc", ({#'previous_object}) }),
({#'do,
({#'=, 'orc, ({#'clone_object, "obj/monster"}) }),
({#'=, 'i, 9}),
({#'do,
({#'call_other, 'orc, "set_level",
({#'+, ({#'random, 2}), 1}) }),
({#'call_other, 'orc,
({#'[,
quote(({"set_aggressive",
"set_ac", "set_short",
"set_al", "set_ep", "set_hp",
"set_race",
"set_alias", "set_name"})),
({#'-=, 'i, 1}) }),
({#'[,
quote(({1, 0, "An orc", -60, 1014,
30, "orc",
"dirty crap", "orc"})), 'i}) }),
'i, 0}),
({#'call_other, 'orc, "load_a_chat", 'chat_chance,
quote(({ "Orc says: Kill 'em!\n",
"Orc says: Bloody humans!\n",
"Orc says: Stop 'em!\n",
"Orc says: Get 'em!\n",
"Orc says: Let's rip out his guts!\n",
"Orc says: Kill 'em before they run away!\n",
"Orc says: What is that human doing here!\n",
})) }),
({#'=, 'n, ({#'*, ({#'random, 3}), 5}) }),
({#'=, 'weapon, ({#'clone_object, "obj/weapon"}) }),
({#'=, 'i, 5}),
({#'do,
({#'call_other, 'weapon,
({#'[,
quote(({ "set_alt_name",
"set_weight", "set_value",
"set_class", "set_name"})),
({#'-=, 'i, 1}) }),
({#'[,
quote(({ "knife", 1, 8, 5, "knife",
"knife", 1, 15, 7, "curved knife",
"axe", 2, 25, 9, "hand axe", })),
({#'+, 'n, 'i}) }) }),
'i, 0}),
({#'transfer, 'weapon, 'orc}),
({#'command,
({#'+, "wield ",
({#'call_other, 'weapon,
"query_name"}) }), 'orc}),
({#'move_object, 'orc, ({#'previous_object}) }),
({#'-=, 'num_orcs, 1}), 0})
})
);
add_prop("get_orc", get_orc);
get_orc = lambda( 0, ({#'funcall, get_orc, 2, 50}) );
add_prop(C_EXTRA_RESET, get_orc);
funcall(get_orc);
}
TWO_EXIT("room/slope", "east",
"room/fortress", "north",
"The orc valley",
"You are in the orc valley. This place is inhabited"
"by orcs.\n"
"There is a fortress to the north, with lot of signs
of orcs.\n", 1)
----------- cut here ------- cut here -------- cut here ----
Procedural elements:
====================
definition of terms:
<block> : zero or more values to be evaluated.
<test> : one value to be evaluated as branch or loop condition.
<result> : one value to be evaluated at the end of the
execution of the form; the value is returned.
<lvalue> : local variable/parameter, global variable, or an
indexed lvalue.
useded EBNF operators:
{ } iteration
[ ] option
forms:
({#', <body> <result>})
({#'? { <test> <result> } [ <result> ] })
({#'?! { <test> <result> } [ <result> ] })
({#'&& { test } })
({#'|| { test } })
({#'while <test> <result> <body>}) loop while test
evaluates non-zero.
({#'do <body> <test> <result>}) loop till test
evaluates zero.
({#'= { <lvalue> <value> } }) assignment
other assignment
operators work, too.
lisp similars:
#', progn
#'? cond
#'&& and
#'|| or
#'while do /* but lisp has more syntactic candy here */
#'= setq
A parameter / local variable 'foo' is referenced as 'foo , a
global variable as ({#'foo}) . In lvalue positions
(assignment), you need not enclose global variable closures in
arrays.
Call by reference parameters are given with ({#'&, <lvalue>})
Some special efuns:
#'[ indexing
#'[< indexing from the end
#'negate unary -
Unbound lambda closures
=======================
These closures are not bound to any object. They are created
with the efun unbound_lambda() . They cannot contain
references to global variables, and all lfun closures are
inserted as is, since there is no native object for this
closure. You can bind and rebind unbound lambda closures to
an object with efun bind_lambda() You need to bind it before
it can be called. Ordinary objects can obly bind to
themselves, binding to other objects causes a privilege
violation(). The point is that previous_object for calls done
from inside the closure will reflect the object doing
bind_lambda(), and all object / uid based security will also
refer to this object.
The following is mostly vapourware.
Well, another application would be that some things in the
driver can be, sort of, microprogrammed. The master object
could set some hooks in inaugurate_master(), like creating the
code for move_object(), using a primitive low_move_object() or
__move_object() or such. All calls of init(), exit(), etc. can
thus be controlled on mudlib level. The driver would do an
implicit bind_lambda() to the victim when the closure is used.
e.g.
({#'?, ({#'=, 'ob, ({#'first_inventory, 'destination}) }),
({#'do,
({#'call_other, 'ob, "init"}),
({#'=, 'ob, ({#'next_inventory, 'ob}) }), 0 })
})
or
({#'filter_objects, ({#'all_inventory, 'destination}), "init"})
/* Won't show init failures due to move/destruct */
is equivalent to
if (ob = first_inventory(destination) ) {
do {
ob->init();
} while(ob = next_inventory(ob) );
}
and its speed is mainly determined by the call_other. Thus,
it shouldn't be noticably slower than the current C code in
move_object().
AUTHOR
MacBeth, Amylaar, Hyp
SEE ALSO
closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)

80
doc/LPC/closures-abstract Normal file
View file

@ -0,0 +1,80 @@
Procedural elements:
====================
definition of terms:
<block> : zero or more values to be evaluated.
<test> : one value to be evaluated as branch or loop condition.
<result> : one value to be evaluated at the end of the execution of
the form; the value is returned.
<lvalue> : local variable/parameter, global variable, or an indexed
lvalue.
<expression>: one value to be evaluated.
<integer> : an integer constant
<string> : a string constant, or 0.
used EBNF operators:
{ } iteration
[ ] option
| alternative
forms:
({#', <body> <result>})
({#'? { <test> <result> } [ <result> ] })
({#'?! { <test> <result> } [ <result> ] })
({#'&& { test } })
({#'|| { test } })
({#'while <test> <result> <body>...}) loop while test evaluates non-zero.
({#'do <body> <test> <result>}) loop till test evaluates zero.
({#'foreach <var> <expr> <body>...}) loop over all values of <expr>.
({#'foreach ({ <var>...<var> }) <expr> <body>...})
({#'= { <lvalue> <value> } }) assignment
other assignment operators work too.
case_label: <integer> | <string> | #'default
generalized_case_label: case_label | <integer> #'.. <integer>
case_label_list: case_label | ({ { generalized_case_label } })
case_delimiter: #', | #'break
({#'switch <expression> { case_label_list <result> case_delimiter } })
Evaluate expression, then evaluate the result form labeled with
the value equal to the value evaluated from expression.
If no matching label exists, the value of #'switch is 0.
({#'catch, <body> [, 'nolog ] [, 'publish ] })
Evaluates the <body> and catches any runtime error. If the symbol
'nolog is also given, a caught error is not logged. If the
symbol 'publish is also given, master::runtime_error() is
called for the caught error.
lisp similars:
#', progn
#'? cond
#'&& and
#'|| or
#'while do /* but lisp has more syntactic candy here */
#'= setq
A parameter / local variable 'foo' is referenced as 'foo , a global
variable as ({#'foo}) . In lvalue positions (assignment), you need not
enclose global variable closures in arrays.
Call by reference parameters are given with ({#'&, <lvalue>})
Some special efuns:
#'[ indexing
#'[< indexing from the end
#'negate unary -
Unbound lambda closures
=======================
These closures are not bound to any object. They are created with the efun
unbound_lambda() . They cannot contain references to global variables, and
all lfun closures are inserted as is, since there is no native object for
this closure.
You can bind and rebind unbound lambda closures to an object with efun
bind_lambda() You need to bind it before it can be called. Ordinary objects
can obly bind to themselves, binding to other objects causes a privilege
violation().
The point is that previous_object for calls done from inside the closure
will reflect the object doing bind_lambda(), and all object / uid based
security will also refer to this object.

202
doc/LPC/closures-example Normal file
View file

@ -0,0 +1,202 @@
CONCEPT
closures example
DESCRIPTION
This document contains small examples of the usage of
(lambda-)closures. For technical details see the closures(LPC)
doc. For hints when to use which type of closure, see the end
of this doc.
Many Muds use 'details' to add more flavour. 'Details' are
items which can be looked at, but are not implemented as own
objects, but instead simulated by the environment.
Lets assume that the function
AddDetail(string keyword, string|closure desc)
adds the detail 'keyword' to the room, which, when look at,
returns the string 'desc' resp. the result of the execution of
closure 'desc' as the detail description to the player.
Now imagine that one wants to equip a room with magic runes,
which read as 'Hello <playername>!\n" when looked at.
Obviously
AddDetail("runes", sprintf( "Hello %s!\n"
, this_player()->QueryName()));
is not sufficient, as the 'this_player()' is executed to early
and just once: for the player loading the room.
The solution is to use closures. First, the solution using
lfun-closures:
private string _detail_runes () {
return sprintf("Hello %s!\n", this_player()->QueryName());
}
...
AddDetail("runes", #'_detail_runes);
or with an inline closure:
AddDetail("runes"
, (: sprintf("Hello %s!\n", this_player()->QueryName()) :)
);
Simple? Here is the same code, this time as lambda-closure:
AddDetail( "runes"
, lambda(0
, ({#'sprintf, "Hello %s!\n"
, ({#'call_other, ({#'this_player})
, "QueryName" })
})
));
Why the extra ({ }) around '#'this_player'? #'this_player
alone is just a symbol, symbolizing the efun this_player(),
but call_other() needs an object as first argument. Therefore,
the #'this_player has to be interpreted as function to
evaluate, which is enforced by enclosing it in ({ }). The same
reason also dictates the enclosing of the whole #'call_other
expression into ({ }).
Note also the missing #'return: it is not needed. The result
of a lambda-closure is the last value computed.
Another example: Task is to reduce the HP of every living in a
room by 10, unless the result would be negative.
Selecting all livings in a room is simply
filter_array(all_inventory(room), #'living)
The tricky part is to reduce the HP. Again, first the
lfun-closure solution:
private _reduce_hp (object liv) {
int hp;
hp = liv->QueryHP();
if (hp > 10)
liv->SetHP(hp-10);
}
...
map_array( filter_array(all_inventory(room), #'living)
, #'_reduce_hp)
or as an inline closure:
map_array( filter_array(all_inventory(room), #'living)
, (: int hp;
hp = liv->QueryHP();
if (hp > 10)
liv->SetHP(hp - 10);
:) );
Both filter_array() and map_array() pass the actual array item
being filtered/mapped as first argument to the closure.
Now, the lambda-closure solution:
map_array( filter_array(all_inventory(room), #'living)
, lambda( ({ 'liv })
, ({'#, , ({#'=, 'hp, ({#'call_other, 'liv, "QueryHP" }) })
, ({#'?, ({#'>, 'hp, 10 })
, ({#'call_other, 'liv, "SetHP"
, ({#'-, 'hp, 10 })
})
})
})
) // of lambda()
);
It is worthy to point out how local variables like 'hp' are
declared in a lambda-closure: not at all. They are just used
by writing their symbol 'hp . Same applies to the closures
parameter 'liv .
The lambda-closure solution is not recommended for three
reasons: it is complicated, does not use the powers of
lambda(), and the lambda() is recompiled every time this
statement is executed!
So far, lambda-closures seem to be just complicated, and in
fact: they are. Their powers lie elsewhere.
Imagine a computation, like for skill resolution, which
involves two object properties multiplied with factors and
then added.
The straightforward solution would be a function like:
int Compute (object obj, string stat1, int factor1
, string stat2, int factor2)
{
return call_other(obj, "Query"+stat1) * factor1
+ call_other(obj, "Query"+stat2) * factor2;
}
Each call to Compute() involves several operations (computing
the function names and resolving the call_other()s) which in
fact need to be done just once. Using lambda-closures, one can
construct and compile a piece of code which behaves like a
Compute() tailored for a specific stat/factor combination:
closure ConstructCompute (object obj, string stat1, int factor1
, string stat2, int factor2)
{
mixed code;
// Construct the first multiplication.
// The symbol_function() creates a symbol for the
// lfun 'Query<stat1>', speeding up later calls.
// Note again the extra ({ }) around the created symbol.
code = ({#'*, ({ symbol_function("Query"+stat1, obj) })
, factor1 });
// Construct the second multiplication, and the addition
// of both terms.
code = ({#'+, code
, ({#'*, ({ symbol_function("Query"+stat2, obj) })
, factor2 })
});
// Compile the code and return the generated closure.
return lambda(0, code);
}
Once the closure is compiled,
str_dex_fun = ConstructCompute(obj, "Str", 10, "Dex", 90);
it can be used with a simple 'funcall(str_dex_fun)'.
DESCRIPTION -- When to use which closure?
First, a closure is only then useful if it needn't to live any
longer than the object defining it. Reason: when the defining
object gets destructed, the closure will vanish, too.
Efun-, lfun- and inline closures should be used where useful, as they
mostly do the job and are easy to read. The disadvantage of lfun- and
inline closures is that they make a replace_program() impossible
- but since such objects tend to not being replaceable at all, this is
no real loss.
Lambda closures are needed if the actions of the closure are
heavily depending on some data available only at runtime, like
the actual inventory of a certain player.
If you use lfun-closures and find yourself shoving around
runtime data in arguments or (gasp!) global variables, it is
time to think about using a lambda-closure, compiling the
value hard into it.
The disadvantages of lambda closures are clear: they are damn
hard to read, and each lambda() statement requires extra time to
compile the closure.
SEE ALSO
closures(LPC), closure_guide(LPC), closures-abstract(LPC)

44
doc/LPC/comments Normal file
View file

@ -0,0 +1,44 @@
NAME
comments
SYNTAX
/* block comment text */
// line comment text <end of line>
DESCRIPTION
Comments are used to stored arbitrary text in the LPC program
source. It is a good idea if some if this text explains the
deeper intentions behind the actual LPC statements.
There are block comments and line comments.
Block comments start with a '/*' and end with a '*/'. They cannot
be nested, so
/* this /* is */ illegal */
will treat '/* this /* is */' as the comment.
Line comments start with '//' and continue until the unescaped(!)
end of the line (as in the new C standard).
It is not possible to next block and line comments within
each other. Meaning: '//' within /* ... */ has no special meaning,
neither does '/*' or '*/' have after a //.
EXAMPLES
/* Simple block comment */
/* Block comments can
span several lines */
// Simple line comment
// Line comments can \
span several lines, too!
//#define LONG_MACRO The unique behaviour \
or line comments regarding the end of line \
can be used for example to comment out a \
large macro with just to keystrokes.

15
doc/LPC/do-while Normal file
View file

@ -0,0 +1,15 @@
NAME
do-while
SYNTAX
do { statement } while(expr);
DESCRIPTION
Execute 'statment' until 'expr' evaulates to 0.
A 'break' in the 'statement' will terminate the loop. A
'continue' will continue the execution from the beginning of
the loop.
SEE ALSO
for(LPC), foreach(LPC), while(LPC), if(LPC), switch(LPC)

39
doc/LPC/ed0 Normal file
View file

@ -0,0 +1,39 @@
NAME
ed0
DESCRIPTION
When in 'ed', the prompt is ':'.
Ed has two modes, command mode and insert mode. The insert
mode has no prompt. You exit the insert mode by typing a
single '.' on a line.
All commands have the following syntax:
X,Ycmd
or
Xcmd
For example:
1,10p
Will print line 1 to 10.
1,5d
Will delete line 1 to 5.
8p
Will print line 8.
A '.' is the "current line". The current line is the last line
referenced. If you want to print last line + 10 more:
.,.+10p
NOTE
These manpages seem a bit antique, though still correct. For a
better detailed help, invoke ed and use the ``h'' command.
Also you could look into the man page for ed(1) on you nearest
Unix box. And for a bit of fun you can try the good old
quiz(6), invoke as ``quiz function ed-command''.
SEE ALSO
ed1(LPC), ed(E)

32
doc/LPC/ed1 Normal file
View file

@ -0,0 +1,32 @@
NAME
ed1
DESCRIPTION
Commands that use a line range:
If no line is given, then curent line is printed.
p Print line.
d Delete line.
l Print line with control characters.
r file Read in a file after the line specified.
s Substitute patterns. See special documentation.
z Print 10 lines.
a Start insert mode after specified line. Exit with
'.'<return>.
i Start insert mode before specified line. Exit with
'.'<return>.
Commands used without line specification:
q Quit. Won't work if file is changed.
Q Quit and discard all changes if not saved.
w Write the file out.
w file Write the file out with name 'file'.
e file Edit a file.
!cmd Give a game command. For example "say Wait, I am busy".
As line numbers '.' is current line, and '$' is last line of
file. Thus '1,$p' will always print all of the file.
SEE ALSO
ed2(LPC)

25
doc/LPC/ed2 Normal file
View file

@ -0,0 +1,25 @@
NAME
ed2
DESCRIPTION
Substitutions are very advanced.
First a simple example:
s/apa/bepa/
This will substitue the 'apa' in current line to 'bepa'.
If an 'p' is appended, you will also immediately see the result.
1,$s/apa/bepa/
Same, but all lines in file. Only first occurence on every line.
Any character can used instead of '/':
s!apa!bepa!g
The 'g' specifies that all occurences of apa on this line are
changed to bepa.
The pattern that are supposed to be replaced, can be a regular
expression. See ed3 about that.
SEE ALSO
ed3(LPC)

30
doc/LPC/ed3 Normal file
View file

@ -0,0 +1,30 @@
NAME
ed3
DESCRIPTION
Searching is done with:
/hello/
Find first line in of after current line.
Just // will repeat the search.
The patterns are regular expressions, where some characters
have a special meaning:
. Match any character.
x* Match any numbers of x (0 or more).
[abc] Match 'a', 'b' or 'c'.
[0-9] Match any digit 0 - 9.
[a-z] Match any lowercase letter.
\x Match 'x' where 'x' can be any character except '('
and ')'.
EXAMPLE
s/ab.d/ABCD/
Substitute any string 'abXd' against 'ABCD' where X can be any
character.
NOTE
This only half the truth, there is lots more magic in the
regexps.
SEE ALSO
regexp(E), ed4(LPC)

12
doc/LPC/ed4 Normal file
View file

@ -0,0 +1,12 @@
NAME
ed4
DESCRIPTION
How to copy from a standard file.
Enter ed. Then do 'r /room/vill_green.c'. Now you have
something in the buffer. Change it into what you want it to
be. Then 'w /players/peter/hall.c'. Or 'w hall.c'.
SEE ALSO
ed5(LPC)

47
doc/LPC/ed5 Normal file
View file

@ -0,0 +1,47 @@
NAME
ed5
DESCRIPTION
= : prints current line
a : append lines
c : change, that is, delate, than insert
d : delete line(s)
E <filename> : discard current buffer and edit the file named
<filename>
e <filename> : like e, but refuse if file has been changed
f : print current filename
f <filename> : set filename
i : insert line(s)
j : with no or one argument: join line with following line
j : with two arguments : join line range given
k<letter> : set mark <letter> to current line. <letter> must
be in the range [a-z] . The mark can be used
thereinafter as a line address, with a leading slash.
(e.g. ka to set mark a, /ap to print marked line)
l : print line with control characters
<start>,<end>m<dest> : move block (lines from start to end)
behind line # dest
<line>m<dest> : move single line
m<dest> : move current line
M : remove Ctrl-M (CR) characters.
p : print line
P : print line
Q : quit, discarding the buffer
q : the same, but refuse if file has been changed since last
write
r : read in file. If no adress is given, at the last insert
position, if also nothing was inserted, at the end of the
buffer
s : substitute
<start>,<end>t<dest> : copy block ( lines from start to end )
behind line position dest
<line>t<dest> : copy single line
t<dest> : copy current line
w : write file
W : write file
x : write file if buffer has been changed since last change,
then quit
z : show approx. a screenful of lines
SEE ALSO
ed6(LPC)

32
doc/LPC/ed6 Normal file
View file

@ -0,0 +1,32 @@
NAME
ed6
DESCRIPTION
This is the list of extended ed commands that Xio unearthed
somewhere, thanks!
a) never use 1,$p to print out an editfile, because you will
be thrown out 'cause of too much text transfereed to you.
b) $: jump to end of file.
c) ?anything? and ?? : search from bottom to up. (like '/'
from beginning to end of file. (also with substitutions,
try out..)
d) ( g/xxx/p search global xxx and print corresponding lines,
/xxx/s/new/old/p : search xxx, substitute new to old in this
line and print out. (try this concatenations with other
commands)
e) 1,nmx ( see ed5 ) , but also: 1,ntx : don't move the lines,
but make a copy of them.
f) x,y w name : save lines x to y to file name (if you don't
know the line numbers : '=' current line number)
g) s/$/text/p : append text to the end of current LINE and
print line
h) s/^/text/p : insert text at beginning og current LINE and
print line

16
doc/LPC/efuns Normal file
View file

@ -0,0 +1,16 @@
CONCEPT
efuns
DESCRIPTION
Efuns are "system calls" in the LPC driver, the C program
which compiles and executes the LPC programs of the mudlib.
These are hardcoded functions which perform basic tasks which
would be ineffective or impossible to be implemented in the
mudlib.
There are efuns for accessing the underlying filesystem, for
creating, moving and destructing objects, for writing
messages to users, for manipulation of LPC data types.
SEE ALSO
efun(E), lfuns(LPC), applied(A), master(M)

81
doc/LPC/escape Normal file
View file

@ -0,0 +1,81 @@
CONCEPT
character escape codes
DESCRIPTION
Character escape codes are used to represent some common
special characters that would be awkward or impossible to
enter in the source program directly. The escape characters
come in two varieties: 'character escapes', which can be
used to represent some particular formatting and special
characters, and 'numeric escapes', which allow a character to
be specified by its numeric encoding.
Escapes begin always with a backslash '\'. If the following
characters could not be treated as a valid escape the backslash
is merely ignored.
The following character escapes are available in LPC (the code
may differ from platform to platform):
\a Code 007 Bell
\b Code 008 Backspace
\e Code 027 Escape
\f Code 012 Formfeed
\n Code 010 Newline
\r Code 013 Carriage-Return
\t Code 009 Tabulator
\\ Code 092 Backslash itself
\' Code 039 Single quote
\" Code 034 Double quote
The single quote may appear without preceding backslash in character
constants, and the double quote may appear without a backslash in
string constants.
The numeric escapes could be used to express a character directly
by its code in binary, octal, decimal or hexadecimal notation.
\0b Beginning of binary notation
\0o Beginning of octal notation
\0x Beginning of hexadecimal notation
\x Beginning of hexadecimal notation
A backslash followed by a digit ([0-9]) which does not map to one
of the above starts an escape in decimal notation.
A numeric escape terminates when N digits have been used up or
when the first character that is not a valid digit in that
notation is encountered. N is 2 for hexadeximals, 3 for
decimals and octals and 8 for binarys.
If the specified code is greater than 255 a warning is issued and
the value modulo 256 is used.
EXAMPLES
Put a newline at the end of user output
"You enter.\n"
Alert the user
"Beeep.\a Wake up\n"
Put a double quote in a string
"You say \"hello\"\n"
Write the line from above
"\"You say \\\"hello\\\"\\n\""
Put a single quote in a string
"You say 'hello'\n"
Some forms to write "abcde"
"abcde"
"ab\99de" (with c's code being 99)
"ab\099de"
"ab\x63de" (99 = 0x63)
"ab\0x63de"
The following string consists of two characters
"\0111" (\011 and 1)
The following string consists of three characters
"\0o090" (\000 and 9 and 0)

36
doc/LPC/for Normal file
View file

@ -0,0 +1,36 @@
NAME
for
SYNTAX
for(init; expr2; expr3) statement;
DESCRIPTION
Execute <init> once. Then, while <expr2> returns a non-zero
value, execute <statement>. Every time <statement> has been
executed, or a 'continue' statement has been executed, execute
<expr3> before next loop.
<init> is usually a series of one or more expressions (remember
that assignments are expressions, too), separated by commas.
Additionally it is also allowed to define new local variables
here and assign them an initial value. The scope of such variables
is the whole for statement.
Examples for legal <init> expressions are:
for (i = 0; ...
for (i = 0, j = 0; ...
for (i = 0, int j = i; ...
for (int j = 4; ...
Illegal <init> expressions are:
for (int i; ... : no value assigned
for (int i += 4; ... : only plain assignments allowed
A 'break' in the 'statement' will terminate the loop. A
'continue' will continue the execution from the beginning of
the loop.
SEE ALSO
foreach(LPC), if(LPC), do-while(LPC), while(LPC), switch(LPC)

71
doc/LPC/foreach Normal file
View file

@ -0,0 +1,71 @@
NAME
foreach
SYNTAX
foreach (<var> : <expr>) <statement>;
foreach (<var>, <var2>, ... ,<varN> : <expr>) <statement>;
/* MudOS compatibility only - not for new code: */
foreach (<var> in <expr>) <statement>;
foreach (<var>, <var2>, ... ,<varN> in <expr>) <statement>;
DESCRIPTION
<expr> is evaluated and has to yield an array, a string or a mapping.
The values of <expr> (in case of the string, the integer values
of the characters) are then assigned one after another to <var>
and <statement> is execute for every assignment.
If <expr> is a mapping, the keys are assigned to <var>, and
the values for each key are assigned in order to <var2>..<varN>.
If there are more values than variable, the extraneous values
are ignored.
If there are more variables than necessary, the unneeded ones
are not changed.
Every <var> specification can declare a new local variable, whose
scope is the whole foreach() statement.
A 'break' in the 'statement' will terminate the loop. A
'continue' will continue the execution from the beginning of
the loop.
WHAT HAPPENS IF <expr> IS CHANGED IN THE LOOP?
If <expr> yields an array:
- assignments to single array elements or to array ranges effect
the values assigned to the variable:
a = ({1, 2, 3})
foreach(x : a) { a[1..2] = ({4, 5}); write(x+" "); }
will write ("1 4 5 ").
- operations which implicitely copy the array (this includes
range assignments which change the size) don't have an effect
on the loop.
If <expr> yields an mapping, the loop will run over the indices
the mapping had at the begin of the loop. Deleted indices are silently
skipped, new indices ignored, but changes of the data of existing
indices are acknowledged.
If <expr> yields a string, it can't be changed anyway.
WARNING
The additional syntax forms using "in" as keyword are meant
to make re-engineering of MudOS objects easier. Do not use them
for newly written code, as they may not be available in future.
EXAMPLES
// Call quit() in all interactive users
foreach(o : users()) o->quit();
foreach(object o : users()) o->quit();
// Print the contents of a mapping <m>
foreach(key, value : m) printf("%O:%O\n", key, value);
foreach(mixed key, mixed value : m) printf("%O:%O\n", key, value);
SEE ALSO
for(LPC)

160
doc/LPC/functions Normal file
View file

@ -0,0 +1,160 @@
CONCEPT
functions
DESCRIPTION
Functions are named blocks of code which are be called with
a number of argument values, and which return a result value
to the caller.
Functions are defined in an object and are also known as
"local funs" or short "lfuns".
DEFINING A FUNCTION
A function definition takes the form
<modifiers> <type> name ( <arguments> ) {
statements...
}
The parts in detail:
- <modifiers> can be any one of "static", "private", "public"
and "protected" (see modifiers(LPC)), optionally combined
with "varargs" (see varargs(LPC)) and/or "nomask".
If not specified, the function behaves as if it was
specified as "public", but this visibility can be restricted
in derived object through non-public inheritance.
- <type> is the type of the result returned by the function.
If specified as "void", the function is compiled to return
the value 0 under all circumstances. If not specified, the
type is assumed to be "mixed", furthermore typechecking is
disabled for this function.
- name is the name of the function, e.g. "short", or "Nice_Try",
under which it is made known.
- <arguments> is a list of variable definitions in the
normal '<type> <name>' style, separated by comma.
Examples: () : no argument taken
(int a) : takes on integer argument
(mixed a, object *b): takes two arguments, one
arbitrary type, one array of objects.
- { statements... } defines the code for this function. This
is a normal block (see block(LPC)) and as such can define
its own local variables.
DECLARING A FUNCTION
A function declaration makes the name and type of a function known
to the compiler with the assertion that the code for this function
will be provided "elsewhere".
The form is:
<modifiers> <type> name ( <arguments> );
Typical uses are:
- to declare in advance functions which are called before they
can be defined; for example if the create() function of an object
calls other functions which are defined after the create().
- to declare functions which will be provided by an inheriting
object.
Calling a declared but undefined function results in a runtime error.
CALLING A FUNCTION
Functions in other objects are called with the call_other() efun,
which can be shortened to '->':
ob->fun(a, b, c)
call_other(ob, "fun", a, b, c)
Functions in the same object are called just by writing their name,
followed by the arguments in parenthesis:
short()
compute(a)
do_that(a, "foo")
If the number of values passed to the function does not match the
number of expected arguments (and if type checking is enabled), the
driver will perform the necessary adaption at call time: excess
values are ignored, missing values are substituted by the number 0.
The values passed to the called function are massaged by the driver
to match the argument list
FUNCTIONS AND INHERITANCE
A "public" or "protected" (== "static") function defined in one
object is also visible in all inheriting objects. The exception from
this rule is when an inheriting child redefines ("overloads") the
inherited function with its own. When compiling with type checking,
the argument list of the redefined function has to match the
original one.
When a function is called, the driver looks for the function first
in the object called, and if not found there, then in the inherited
objects.
To explicitely call an inherited function (useful when a redefining
functions wants to use the original one), the "::" operator is used:
::create()
::compute(a)
The named function is searched only in the inherited objects, and
the first found is used.
If the function is inherited from several objects and a specific
one is to be called, the "::" can be extended to contain the
partial or full name of the inherited object:
inherit "/obj/cooker";
inherit "/obj/container";
tainer::create()
container::create()
"tainer"::create()
"container"::create()
"obj/container"::create()
"/obj/container"::create()
all call the create() in the container inherit. Note that the
name given to the :: operator is matched against the ends of
the inherited names.
One special form of this call is
efun::find_object()
which bypasses any redefinition of an efun (here find_object())
and directly calls the efun itself. This is only possible for
efun-redefinitions which do not use the "nomask" modifier.
Additionally, a call to a function inherited from several objects
can be instructed to call _all_ inherited functions through the
use of the wildcards "*" (match any number of arbitrary characters)
and "?" (match one arbitrary character):
inherit "/obj/cooker";
inherit "/obj/container";
"*"::create()
"co*"::create()
"*er"::create()
all call both inherited create()s. The function called this way
must not take arguments, and the single results from all calls are
combined into one array used as final result. If there is no such
function inherited at all, the statement will just return
an empty array.
SEE ALSO
types(LPC), modifiers(LPC), varargs(LPC), references(LPC),
call_other(E), simul_efun(C), call_out(E)

39
doc/LPC/if Normal file
View file

@ -0,0 +1,39 @@
NAME
if
SYNTAX
if (expr1) statement1;
else if (expr2) statement2;
...
else if (exprN) statementN;
else statementX;
DESCRIPTION
The if() statement implements the conditional execution of statements.
The expressions 'expr1' .. 'exprN' are evaluate in the order they
appear until one of the expressions returns non-0 ('true'). At that
point, the statement associated with the expression is executed, and
the program continues after the if() statement. If none of the
expressions evaluate to 'true', the statementX in the 'else'-branch
is executed.
Both the 'else if' branches and the 'else' branch are optional, and
there can be any number of 'else if' branches - but there must be one
'if' branch, and the branches must be in the order given above.
Any 'else' or 'else if' always relates to the immediately preceeding
'if' resp. 'else if' conditional. This means that
if (a)
if (b) do_b;
else do_c;
is interpreted as
if (a) {
if (b) do_b;
else do_c;
}
SEE ALSO
for(LPC), foreach(LPC), do-while(LPC), while(LPC), switch(LPC)

9
doc/LPC/inherit Normal file
View file

@ -0,0 +1,9 @@
NAME
inherit
DESCRIPTION
The concept of inheritance and the inherit statement are
explained in detail in the man page for ``inheritance''.
SEE ALSO
inheritance(LPC)

38
doc/LPC/initialisation Normal file
View file

@ -0,0 +1,38 @@
CONCEPT
INITIALIZATION
DESCRIPTION
There are two different flavours of initialisations, selectable
in config.h :
i) #undef INITIALIZATION_BY___INIT
------------------------------------
Initialisation is done at compile time. This is fast and costs
no extra code in the program.
Allowed expressions currently include integer literals,
string literals, integer operators, string addition, bracketing,
array constructors, the empty mapping and order_alist().
When an object with initialised variables is cloned or inherited,
all initialised variables are copied from the blueprint.
A special application of this feature is to have an initialised
non-empty array or a mapping; it will be shared by all clones or
inheriting objects unless an assignment to the variable -
as opposed to an assignment to an element of the array/mapping -
is done in all clones etc.
To prevent unauthorised changes in initialised arrays/mappings,
you can declare the variables as private or use
a nomask reset/create that checks for undesired inheritance.
ii) #define INITIALIZATION_BY___INIT
-------------------------------------
Creates a function names __INIT() from all variable
initialisations and from calls to __INIT() in all inherited
objects, and runs this function at object creation time.
Any efun can be used in the expressions for variable
initialisations, even ones with severe side effects, like
destruct() or shutdown(). The code created for __INIT() is
a little worse than a medium-skilled lpc-programmer would
generate, because it is scattered all over the program.

31
doc/LPC/lfuns Normal file
View file

@ -0,0 +1,31 @@
CONCEPT
lfuns
DESCRIPTION
A lfun is a LPC function within an object which is public and can
be called by other objects. In OO terms, lfuns are "methods"
which you can send "messages" to.
Calling lfuns is done by using the efun call_other(), which
takes as arguments the object in which the lfun is to be called,
the name of the lfun to be called in the object, and additional
and optional arguments.
An example looks like this:
call_other(drink, "QueryShort");
This call may also be written as
drink->QueryShort();
This means call_other(object, "function", args...) can also be
written as object->function(args...). The second form is
preferred as it is easier to read.
Some lfuns have a special meaning for the LPC driver, because
they are applied by the interpreter instead from an LPC object.
To distinguish those, they are called ``applied lfuns''.
SEE ALSO
efuns(LPC), efun(E), applied(A), master(M), call_other(E)

9
doc/LPC/lpc Normal file
View file

@ -0,0 +1,9 @@
NAME
lpc
DESCRIPTION
This directory contains man pages about various aspects of the
LPC language as it is provided by Amylaars parser/driver.
SEE ALSO
concepts(C), driver(D), efun(E), applied(A), master(M)

450
doc/LPC/mappings Normal file
View file

@ -0,0 +1,450 @@
CONCEPT
mappings
LAST UPDATE
Mon, 15 Mar 1999
DESCRIPTION
A step-by-step introduction to mappings:
----------------------------------------
1. What is a mapping?
A mapping is a datatype which allows to store data associated to a key.
In other languages they are also known as 'dictionaries' or 'alists'.
There are also alists in LPC but they are not a separate datatype but are
implemented on top of arrays. Alists are the predecessors of mappings.
The keys and the values can be of any type. But most common datatypes
for keys are strings, integers and objects. Others like arrays, mappings
or closures aren't a good choice because comparision between i.e. arrays
often returns false even if they equal in content. This is because the
driver compares i.e. two arrays by their internal pointers and not by
their content. The reason for this is simple: speed.
Mappings are allways treated as references when passing them to
functions. This means when you pass a mapping to another object and this
object modifies the mapping the modification will take place in a global
scope - visible to all objects holding this mapping in a variable.
2. What are mappings good for?
The term 'dictionary' probably describes the use of a mapping best.
Opposed to arrays mappings don't have a specific order. They provide a
mechanism to create a set of associations between values. Such an
association consists of a unique key and data that is identified by the
key. Think of a dictionary where you have a word and a definition of
it. You use the word to lookup its definition.
Mappings can be used i.e. to hold aliases for commands. The key would
then be the name of the alias and the data the command(s) behind an
alias. Or they can be used for the exits of a room. The keys would be
the directions where one can go to and the associated data would be the
file names of the rooms. But mappings can also be used as a kind of a
sparse array. A sparse array is an array where most of the elements
aren't used (occupied by 0). I.e. if you want to store values at the
position 0, 13 and 37642 of an array you would have to create an array
with a size of at least 37643. This costs a lot of memory so a mapping
would be more useful because you would then use the numbers 0, 13 and
37642 as a key and not as an index to a position (actually the keys of a
mapping are sometimes called indices but this is just because the way
data is accessed in a mapping is similar to arrays: by the [] operator).
This also allows to query all occupied positions of a sparse array by
querying for all the keys of the mapping opposed to an array where you
have to iterate over all elements.
3. How do I create a mapping?
There are several ways to do so. The most convenient is the following:
mapping map;
map = ([ key0: value00; ...; value0n,
... : ... ; ...; ... ,
keyn: valuen0; ...; valuenn ]);
As you can see, a key may have more than one value assigned. But the
amount of values per key must always be equal. It is even possible to
have mappings without any values!
Another method is to use the efun mkmapping(). This efun gets two
arguments with the first beeing an array of keys and the following beeing
arrays of values:
mapping map;
map = mkmapping (({ key0 , ..., keyn }),
({ value00, ..., value0n }),
({ ... , ..., ... }),
({ valuen0, ..., valuenn }));
If the efun only gets one argument, then this argument will be taken as
an array of keys and a mapping without values will be returned.
An empty mapping can be created by using the above described methods by
simply ommitting the keys and values:
mapping map;
map = ([]);
or:
map = mkmapping(({}), ({}));
Or by using the efun m_allocate(). This efun gets as first
argument the amount of keys which will be added soon and an optional
second argument specifying the width of the mapping:
map = m_allocate(n, width);
The value <n> may be a bit confusing since mappings shrink and grow
dynamically. This value just tells the driver how 'long' this mapping is
going to be so proper memory allocations will be performed to reduce
the overhead of memory reallocation. I.e. if you want to read in a file
and store the read data in a mapping you probably know the amount of
keys. So you allocate a mapping with this efun and tell the driver how
much memory should be allocated by specifing a proper <n> value.
Thus causing a speedup when adding the read data to the mapping
afterwards. The <width> just specifies how many values per key this
mapping is going to have. If no width is given, 1 will be taken as
default.
An empty mapping created with '([])' will always have a width of 1. To
create empty mappings with other widths, write it as
map = ([:width ]);
<width> can be any expression returning an integer value (including
function calls), and in fact this notation is just a fancy way of
writing
map = m_allocate(0, width);
4. How can I modify the data of a mapping?
Adding a new key is similiar to modifying the associated data of an
existing key:
map += ([ key: value0; ...; valuen ]);
Or in case only a single value should be modified:
map[key, n] = valuen;
If <n> is out of range or if <key> doesn't exists and <n> is greater
than 0 an "Illegal index" error will be reported. If <n> is equal to 0 or
the mapping only has a single value per key one can abbreviate it with:
map[key] = value;
If there is no <key> (and <n> is equal to 0 or not specified at all) a
new one will be added automatically.
Deletion of a key is done with the -= operator or the efun
m_delete(). A mapping can only be substracted by one without any values:
map -= ([ key ]);
or:
map -= ([ key0, ..., keyn ]);
The efun takes a mapping as first and a key as second argument:
m_delete(map, key);
The efun m_delete() returns the mapping but because mappings are
handled as references there is no need of an assignment like:
map = m_delete(map, key);
5. How can I access the data stored in a mapping?
This can be done by:
valuen = map[key, n];
Or in case of a mapping with just one value per key:
value0 = map[key];
If there is no <key> in the mapping and <n> is 0 or not specified at
all (which is the same) a 0 will be returned or if <n> is greater than 0
an "Illegal index" error will be reported.
6. How can I test for the existance of a key?
A return value of 0 is sufficient for most applications but sometimes
the ambiguity between an existing value of 0 and a nonexisting key can
lead to a problem. Therefore one can use the efun member() or
mapping_contains() to check if there actually is a key in the mapping:
if (member(map, key)) {
...
}
or:
if (mapping_contains(&value0, ..., &valuen, map, key)) {
...
}
This also shows how one can retrieve all values associated to a key
from a mapping in a single step. The '&' is the reference operator which
is neccesary to let the efun store the values in the variables.
In case of mappings with no values, the efun member() and
mapping_contains() are equal in their behaviour and their way of calling
because mapping_contains() won't get any reference variables to store the
values in (obviously, because there aren't any).
Also normally member() is known to return the postion of an element in
a list (i.e. a character in a string or data in an array) and if an
element couldn't be found -1 is returned. But in the case of mappings
there are no such things as order and postion. So member() only returns 0
or 1.
7. How can I copy a mapping?
A mapping can be copied with the + operator or by the efun
copy_mapping():
newmap = ([]) + map;
or:
newmap = copy_mapping(map);
A mapping should only be copied when it is neccesary to get an own copy
of it that must not be shared by other objects.
8. How can I get all keys of a mapping?
The efun m_indices() gets a mapping as argument and returns an array
holding all keys defined in this mapping:
keys = m_indices(map);
9. How can I get all the values of a mapping?
The efun m_values() gets a mapping as argument and returns an array
holding all the first (second, ...) values of it.
values0 = m_values(map); returns the first values
values0 = m_values(map, 0); dito
values1 = m_values(map, 1); returns the second values
etc
10. How can I determine the size of a mapping?
Because a mapping is a kind of rectangle it has two sizes: a length and
a width. There are three different efuns to query these values. The first
two are the efuns sizeof(), which returns the amount of key-value
associations (the length of a mapping), and widthof(), which returns the
number of values per key (the width). The third is the efun get_type_info().
get_type_info() is meant to be a function to identify a datatype. Its
return value is an array of two numerical values. The first specifies
the datatype of the argument and the second is a datatype dependend
value. In the case of a mapping the first value is T_MAPPING (which is a
value defined in <lpctypes.h>) and the second the amount of values per
key (a.k.a. columns or the width of the mapping - actually it would be
correct to say that the width of a mapping is the amount of columns plus
one for the keys but this is uncommon).
11. What is the best method to iterate over a mapping?
First of all the main purpose of a mapping is not meant to be a set of
data to iterate over. Afterall the keys in a mapping have no specific but
a random order (at least on the LPC side). But still it is possible and
sometimes even neccesary to do so.
If all key-value associations should be processed then one should use
walk_mapping(). If all keys of a mapping should be processed to create a
new mapping being a subset of the given one, then filter_mapping() should
be used. If all keys are going to be processed and to create a new
mapping with the same set of keys as the given mapping, then one would
use map_mapping(). But in the case of an iteration that should/can stop
even if not all data is processed it is probably wise to iterate over the
mapping by first querying for the keys and then to iterate over them with
a for() or a while() loop and querying the values by 'hand'.
The efun walk_mapping() gets a mapping as first argument and the name
of a function as second one. All the following arguments are treated as
extras which will be passed to the function specified with the 2nd
argument. Instead of a string for the name of a function a closure can be
used, too. Nothing will be returned:
...
walk_mapping(map, "func", xarg0, ..., xargn);
...
void func(mixed key, mixed value0, ..., mixed valuen,
mixed xarg0, ..., mixed xargn) {
...
}
func() will be called for all key-value associations and gets as first
argument the key. The next arguments are the values behind the key and
are passed as references. The rest of the passed arguments are those
specified as extras. Because the values are passed as references (opposed
to copies) it is possible to modify them from inside func() by simply
assigning new value to the variables <value0>, ..., <valuen>.
The efun filter_mapping() calls a function for each key in a mapping
and creates a new mapping which only contains key-value associations for
which the called function returned true (not equal 0 that is). The first
argument is the mapping to iterate over and the second is a function name
given as a string or a closure:
...
submap = filter_mapping(map, "func", xarg0, ..., xargn);
...
int func(mixed key, mixed xarg0, ..., mixed xargn) {
...
}
func() gets as first argument the key and the others are those passed
as extras to filter_mapping().
The efun map_mapping() gets a mapping as first argument and a string as
a function name (or again a closure) as second argument. Any additional
arguments are again used as extras that will be passed to the iteration
function. This efun returns a new mapping with the same keys as the given
one. The values returned by the function that is invoked for each key
will be used as the associated data behind each key of the new mapping:
...
newmap = map_mapping(map, "func", xarg0, ..., xargn);
...
mixed func(mixed key, mixed xarg0, ..., mixed xargn) {
...
}
func() gets as first argument the key and the others are those passed
as extras to map_mapping().
Because a function can only return a single value (even when it is an
array) it restricts the use of map_mapping() to only allow creation of
mappings with a single value per key.
12. Is it possible to join/intersect/cut mappings with another?
Joining mappings is only possible, if they have the same width (amount
of values per key). One can use the + and += operator:
map = map1 + map2 + ... + mapn;
map += map1 + map2 + ... + mapn;
Intersection of two mappings is only possible by using
filter_mapping(). There is no efun or operator which features this. The
'easiest' way may be the following function:
mapping intersect_mapping(mapping map1, mapping map2) {
closure cl;
cl = lambda(({ 'key }), ({ #'member, map2, 'key }));
return filter_mapping(map1, cl, map2);
}
This function returns a new mapping which consists of all key-value
associations of <map1> for which an equal key could be found in
<map2>. This function uses a closure which returns 0 or 1 depending on
wether a key from <map1> is contained in <map2> or not.
Cutting out all key-value associations of a mapping for which a key
could be found in another mapping can be done by using the - and -=
operator:
mapping cut_mapping(mapping map1, mapping map2) {
return map1 - mkmapping(m_indices(map2));
}
Because a maping can only be substracted by one without any values we
first have to create such by using m_indices() and mkmapping().
13. What are those mappings without any values (besides keys) good for?
Because the way how the driver searches for a key in a mapping is
rather fast, those mappings can be used as a set of elements with a fast
method for testing if an element is contained in the set. This technique
is called hashing (further explanation would lead too far) which is
faster than searching for values in array (which is done in a linear
fashion).
Another (maybe more pratical) use of these mappings are to create a
array of unique values out of an array with several equal values:
uniques = m_indices(mkmapping(array));
mkmapping() uses <array> to create a mapping without any values but
just keys. And because a mapping can only have unique keys all multiple
values in <array> are taken as one. The call of m_indices() then returns
an array of these unique keys. Actually we only make use of those
mappings temporarily.
14. How can I convert an alist into a mapping and vice versa?
There are no special efuns which handle such conversions. But it can be
done by the following functions:
mapping alist_to_mapping(mixed *alist) {
return apply(#'mkmapping, alist);
}
The efun apply() takes a closure and an array of values and passes each
element of the array as an argument to the closure. Because an alist
consists of an array of arrays with the first beeing the list of keys and
the others the values associated to each key passing them as arguments to
the efun closure #'mkmapping via apply() causes the creation of a mapping
out of an alist.
mixed *mapping_to_alist(mapping map) {
mixed *alist;
symbol *vars;
string var;
closure cl;
int width;
width = get_type_info(map)[1];
alist = allocate(width + 1);
vars = allocate(width + 2);
for (var = "a"; width; var[0]++, width--) {
alist[width] = ({});
vars[width] = quote(var);
}
alist[0] = ({});
vars[0] = 'key;
vars[<1] = 'alist;
cl = lambda(vars, ({ #'=, 'alist, ({ #'insert_alist }) + vars }));
walk_mapping(map, cl, &alist);
return alist;
}
This function is a bit more complicated than the other and detailed
description would lead too far of the topic. This function has one
restriction: it can only turn a mappings with up to 26 values per key
into an alist. But this should be sufficient for probably all
applications which use mappings.
And Hyps further comment on this:
The function mapping_to_alist() is also not that
clever because insert_alist() allways creates a new
alist. A second (optional) argument to m_values() to
specify the value column would be better. Besides
this, the conversion of a mapping into an alist could
be done by to_array().
BUGS
Mappings can't use lfun or identifier closures as keys. This
restriction has been corrected in 3.3 .
HISTORY
The ([:width ]) notation was added in LDMud 3.2.9 .
SEE ALSO
alists(LPC), closures(LPC), mkmapping(E), walk_mapping(E)

177
doc/LPC/modifiers Normal file
View file

@ -0,0 +1,177 @@
CONCEPT
modifiers
DESCRIPTION
A modifier changes the syntactic and/or semantic behaviour of
an object-global variable or a function in an object.
The existing modifiers are described below.
To use a modifier just prepend it to the declaration. If several
modifiers are to be used their order does not matter:
private int bar; // example for a variable
protected nomask int foo() { return 3; } // example for a function
For functions:
~~~~~~~~~~~~~~
private -- such functions can only be called with an internal
call from within this file. Not even inheriting
objects can call these functions. You can nevertheless
build an lfun-closure with #' out of a private function.
protected -- such functions can be called from within the object,
or from inheriting objects; but in neither case
with call_other(). It is possible to create #' closures
or use symbol_function() from within the object.
Its use is preferred over the older "static".
static -- such functions can be called from within the object
in either way (internal call or with call_other()).
Inheriting objects can call such functions.
But it is not possible to call static functions from
other objects via call_other().
Note that an add_action() is treated like a call
from within the object except the player who got the
add_action() was forced (thus it is a simple way to
secure an add_action() against forces, although this
method has the severe disadvantages of raising an error
at the force so better use the security system).
Also efuns like call_out() or input_to() can call
these functions if given as a string.
public -- this is the default type. Such functions can be called
from within the file as well as from inheriting objects
and other objects via call_other().
To declare a function public only results in the
impossibility to change the accessibility at the
inherit statement (see below). No error will occur,
only the type will not be modified by the inherit
statement.
nomask -- such functions cannot be overridden by inheriting
objects. If you have the fun foo() defined in your
object and inherit an object which also has declared
a function foo() and this nomask, you will get an
compile error if you try to load your object.
Furthermore a shadow will fail if it tries to shadow
a nomask declared function.
varargs -- this changes the syntax of the function in a way that
not all of the arguments in the declaration must be
given at the call. This is often very usefull if some
of the arguments shall be omitable (the omitted
arguments are set to 0 if the function is called with
fewer arguments than specified).
This is mainly within the object really necessary;
call_other()s usually (that is if they do not have a
certain pragma ('man pragma')) do not need the called
function to be declared varargs to omit any arguments,
but it is good style to use this modifier to document
the code by this.
For object-global variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private -- such variables can only be accessed from within the
same object. Not even inheriting objects can access
private variables.
It is a good style to declare all internal variables
private to prevent inheriting objects from accessing
the variables directly without using functions.
nosave -- such variables are neither stored with save_object()
nor restored with restore_object(). This can be very
useful if you want a room to use save_object() and
restore_object() to save your own defined variables
but not the hundreds of variables inherited from a
room-class (e.g. /complex/room). You then use the modifier
at the inherit statement (see below).
Note that nosave and private do not overlap in any
way. They are absolutely independant.
The driver may be compiled to not recognize this
keyword ('static' is then to be used). If 'nosave'
is available, the macro __LPC_NOSAVE__ is defined.
static -- the old name for 'nosave'. Its use is deprecated.
public -- declares the variable public. It cannot be declared
private or static by inheriting. No error will occur,
only the type will not be modified by the inherit
statement.
It is no good style to let inheriting objects have access to
internal variables so declare them as private and offer functions
to query and change the variables if possible.
It is also possible to redeclare all variables and/or functions
of an inherited object for the own object at the inheriting
statement:
private functions nosave variables inherit "complex/room";
public variables inherit "complex/room";
private functions inherit "complex/room";
To redeclare a function or a variable declared public in the
inherited object to be private or protected is not possible.
There also exists a modifier explicitly for the inherit statement:
virtual -- inherits the given object virtually. This only makes
sense in a complex inherit tree.
If an object is inherited normally (not virtually)
twice somewhere in the inherit tree the intern
variables exist twice. If inherited virtually they
exist only once.
Example:
A inherits B and C.
B inherits D.
C inherits D.
If the inheritance of D is virtual in B and C
D's variables exist only once in A. If A changes
D's variables via functions of B this also changes
the variables of D as known by C.
virtual: non-virtual:
A A
/ \ / \
B C B C
\ / | |
D D D
To simplify the adoption of existing code, LPC allows to specify
a default visibility for functions and variables, using a syntax
similar to the inherit syntax:
default private;
All variables and functions are by default private.
default private variables public functions;
All variables are by default private, but functions are public.
Only the modifiers 'private', 'public' and 'protected' (and 'static'
for functions only) are allowed here.
The default visibility thus set affects only variables/functions with
no explicite visibility:
default private;
int private_var;
public int public_var;
The definition is valid from the point of the 'default' statement
until the end of the file, or until the next 'default' statement:
default private;
int private_var;
default public;
int public_var;
Note that this default visibility does not affect inherits.
HISTORY
The modifier 'static' for variables was renamed to 'nosave'
with LDMud 3.2.8. 'static' is still recognized as an alias.
The default visibility was added in LDMud 3.2.9 as experimental
feature.
SEE ALSO
closures(LPC), inheritance(LPC), functions(LPC), types(LPC)

203
doc/LPC/operators Normal file
View file

@ -0,0 +1,203 @@
NAME
operators
DESCRIPTION
These are the operators availailable in LPC. They are listed
in the order of precedence (low priority first):
expr1 , expr2 Evaluate 'expr1' and then 'expr2'. The
returned value is the result of 'expr2'. The
returned value of 'expr1' is thrown away.
var = expr Evaluate 'expr', and assign the value to
'var'. The new value of 'var' is the result.
var += expr Assign the value of 'expr' + 'var' to 'var'.
This is mostly equivalent to "var = var + expr".
var -= expr Similar to '+=' above.
var &= expr
var |= expr
var ^= expr
var <<= expr
var >>= expr
var >>>= expr
var *= expr
var %= expr
var /= expr
var &&= expr
var ||= expr
expr1 ? expr2 : expr3
Evaluates 'expr1' and branches according to
its truth value. If it is true, the 'expr2' is
evaluated and returned as result, else
'expr3'.
expr1 || expr2 The result is true if 'expr1' or 'expr2' is
true. 'expr2' is not evaluated if 'expr1' was
true.
expr1 && expr2 The result is true i 'expr1' and 'expr2' is
true. 'expr2' is not evaluated if 'expr1' was
false.
expr1 | expr2 The result is the bitwise or of 'expr1' and
'expr2'.
expr1 ^ expr2 The result is the bitwise xor of 'expr1' and
'expr2'.
expr1 & expr2 The result is the bitwise and of 'expr1' and
'expr2'.
For arrays and strings, the intersection set
(all elements resp. characters from expr1 which
which are also in the expr2) is computed.
Note: "aab" & "a" -> "aa"
but ({ 'a','a','b' }) & ({ 'a' }) -> ({ 'a' })
Eventually the array behaviour will be changed
to match the string behaviour.
Mappings can be intersected with another mapping
or an array. The resulting mapping holds all
those entries from the first array, which are
also mentioned in the second mapping (as index)
resp. in the array.
expr1 == expr2 Compare values. Valid for strings, numbers,
objects and closures.
expr1 != expr1 Compare values. Valid for strings, numbers,
objects and closures.
expr1 > expr2 Valid for strings and numbers.
expr1 >= expr2 Valid for strings and numbers.
expr1 < expr2 Valid for strings and numbers.
expr1 <= expr2 Valid for strings and numbers.
expr1 << expr2 Shift 'expr1' left by 'expr2' bits; the sign
bit is not preserved.
expr1 >> expr2 Shift 'expr1' right by 'expr2' bits.
This shift preserves the sign of 'expr1'.
expr1 >>> expr2 Shift 'expr1' right by 'expr2' bits.
This shift does not preserve the sign of 'expr1',
instead it shifts in 0 bits.
expr1 + expr2 Add 'expr1' and 'expr2'. If numbers, then
arithmetic addition is used. If one of the
expressions are a string, then that string is
concatenated with the other value.
If the expressions are arrays, the result is
the right array appended to the left.
If the expressions are mappings of equal width,
the result is merger of the two mappings. If one
key exists in both mappings, the element from the
right mapping appears in the result. If the two
mappings are of different width, the result is
<expr1> if non-empty, and <expr2> otherwise.
expr1 - expr2 Subtract 'expr2' from 'expr1'. Valid for
numbers, strings, arrays, mappings.
For arrays and strings, all occurences of the
elements resp. characters in 'expr2' are removed
from 'expr1', and the result is returned.
For mapping, all occurances of elemens in 'expr1'
which have a matching key in 'expr2' are removed, and
the result is returned.
expr1 * expr2 Multiply 'expr1' with 'expr2'.
If strings or arrays are multiplied with a number
(zero or positive), the result is a repetition of the
original string or array.
expr1 % expr2 The modulo operator of numeric arguments.
expr1 / expr2 Integer division.
++ var Increment the value of variable 'var', and
return the new value.
-- var Decrement the value of variable 'var', and
return the new value.
- var Compute the negative value of 'var'.
! var Compute the logical 'not' of an integer.
~ var The boolean 'not' of an integer.
( type ) var Return the value of <var> converted to <type>.
<type> can be 'string', 'int', 'object', 'float'
or 'int*'. <var> must be of a specific type
for a conversion to take place; if <var> is 'mixed'
or unknown, the cast is purely declarative.
Also, if the declared type of <var> is that of <type>,
the value is not changed.
NB. The literal number 0 is of unknown type, as
it doubles as 'not initialized' for strings, objects,
and arrays.
The operator acts like the efuns
to_string(), to_int(), to_object(), to_float()
and to_array(). It is advisable to use the
efuns directly instead of the cast.
({ type }) var <var> is now assumed to have the type <type>.
This is purely declarative, the actual value
of <var> is not changed.
var ++ Increment the value of variable 'var', and
return the old value.
var -- Decrement the value of variable 'var', and
return the old value.
expr1[expr2] The array or mapping given by 'expr1' is
indexed by 'expr2'.
expr1[expr2..expr3] Extracts a
piece from an array or string.
expr2 or expr3 may be omitted, default is the begin
or end of expr1.
Negative numbers for expr2 or expr3
mean ``count from before the beginning'', i.e.
foo[-2..-1] is an empty array or string.
foo[<2..<1] gives the 2nd and last element of
the array resp. chars of the string.
expr1->name(...) The symbolic form of call_other(). 'expr1'
gives either an object or a string which is
used as the file_name of an object, and calls
the function 'name' in this object.
ident::name(...)
Call the inherited function 'name' with the
given parameters in the parent 'ident'.
'ident' may be given as string containing the
full pathname, or as identifier containing the
pure basename.
If 'ident' is omitted, the last inherited
function of this 'name' is called.
({ }) Array constructor.
([ ]) Mapping constructor.
NOTE
The closure operators are not described here.
HISTORY
LDMud 3.2.9 added '>>>', '>>>=', '&&=' and '||='.
LDMud 3.2.10 extended '&' to mappings.
SEE ALSO
arrays(LPC), alists(LPC), mappings(LPC), closures(LPC)

107
doc/LPC/pragma Normal file
View file

@ -0,0 +1,107 @@
NAME
pragma
DESCRIPTION
The preprocessor directive #pragma can be used to select
several compilation options. Multiple options can be selected
in one #pragma directive by separating them with commas.
combine_strings: Perform the addition of string literals at
compile time instead of run time, e.g. "foo"+"bar"
will be compiled as "foobar". Like the concatenation
of adjacent string literals ("foo" "bar" is compiled
as "foobar" as well) already done by the preprocessor,
the combine_strings will save memory, because the combined
strings can be shared between a blueprint and it's clones,
and the strings also have a good chance to be entered
into the global shared-string-table.
no_combine_strings: Contrary to combine_strings, may be used to
deactivate a combine_strings pragma.
no_clone: The blueprint object can't be cloned.
no_inherit: The program can't be inherited.
no_shadow: The program can't be shadowed (similar to declaring
all functions as 'nomask').
weak_types: no type checking (this is the default).
strict_types: all functions must be declared with argument
prototypes, and the return values of call_other() must
be casted.
strong_types: all functions must be declared with complete
types of returnvalue and parameters.
save_types: the declaration data is kept after compilation and
checked at runtime. This is important for type-safe
inheritance.
verbose_errors: upon a compilation error, the driver displays
the actual context of the errorenous text. This is
helpful with errors within long expressions.
no_local_scopes, local_scopes: when local scoping is turned
off, local variables are visible even outside
their defining blocks. The setting of this pragma is
considered upon entry into a function only. By default,
local scoping is on.
local_scopes: local variables are visible only inside their
defining blocks.
pedantic: Certain warnings are treated as errors:
- failure to pass enough arguments to simul efuns
sloppy: Turns off pedantic (the default).
warn_deprecated: Use of deprecated efuns causes the compiler
to issue a warning.
no_warn_deprecated: Turns off warn_deprecated (the default).
warn_empty_casts: A cast of a value to its own type generates
a warning (the default). Exception are casts to type
'mixed'.
no_warn_empty_casts: Turns off warn_empty_casts.
warn_function_inconsistent: If an inherited function is
overloaded with inconsistent return types or arguments,
a warning is generated; or if pragma_pedantic is in effect,
an error. By default this is active.
no_warn_function_inconsistent: An inherited function can
be overloaded with inconsistent return types or arguments,
as long as pragma_pedantic is not in effect.
This pragma is meant to easen the adaption of legacy
mudlib code - in general one should fix the warnings,
not turn them off.
When an object is compiled with type testing (#pragma
strict_types), all types are saved of the arguments for that
function during compilation. If the #pragma save_types is
specified, then the types are saved even after compilation, to
be used when the object is inherited.
The following two pragmas are available if the driver was
compiled with DEBUG and TRACE_CODE options:
set_code_window: Sets an offset to the current program writing
position. Use this BEFORE a piece of code where you
want to check to what bytecodes it is compiled.
show_code_window: shows some bytes starting at or near the
last point set_code_window was called.
EXAMPLES
#pragma strict_types
#pragma no_clone, no_inherit
HISTORY
LDMud 3.2.7 added local_scopes, no_local_scopes, no_clone
and no_inherit.
LDMud 3.2.8 added weak_types, pedantic and sloppy.
LDMud 3.2.9 added (no_)warn_deprecated.
LDMud 3.2.9 allowed to specify multiple pragmas in one directive,
and added (no_)warn_deprecated.
LDMud 3.2.10 added (no_)warn_empty_casts.
Starting with LDMud 3.2.10, #pragma xxx_types in an included file are
no longer valid just until the end of the file, but remain active
when processing returns to the including file.
LDMud 3.2.11 added (no_)warn_function_inconsistent.
SEE ALSO
inheritance(LPC), initialisation(LPC), objects(C),
operators(LPC)

23
doc/LPC/preprocessor Normal file
View file

@ -0,0 +1,23 @@
NAME
preprocessor
DESCRIPTION
The LPC driver understands the following preprocessor
directives:
#include, #define, #if, #ifdef, #ifndef, #else, #elif,
#endif, #undef
same as in ANSI C
#line <num>
line numbers start at <num> with the next line
#echo
the rest of the line is printed to the error output
(stderr), thus can be captured into a log file
#pragma
see the separate man page
SEE ALSO
pragma(LPC), predefined(D)

49
doc/LPC/references Normal file
View file

@ -0,0 +1,49 @@
CONCEPT
references
DESCRIPTION
Call by reference can be used to have a function that passes
more than one value to the caller, without using arrays that
have to be unpacked thereinafter.
There is nothing special to declare in the calling function,
you simply do an assignment to a parameter of the function.
The caller has to pass references explicitely; this is done by
prefixing an lvalue with '&' .
To pass a reference to an element of an array, you have to
enclose the indexed lvalue in round brackets.
EXAMPLE
void assign(mixed destination, mixed source) {
destination = source;
}
void extract_number(int destination, string source) {
sscanf(source, "%d", destination);
}
void test() {
int i;
float f;
mixed *a;
extract_number(&i, "42 palantirs");
assign(&f, 3.141592653589793);
assign(&a, ({ i, f }));
assign(&(a[<0..<1]), ({1,2,3,"sink","x","y","x"}));
assign(&(a[5][0]), 'w');
assign(&(a[5][<1]), 'g');
printf("%O", a));
}
({ /* sizeof() == 9 */
42,
3.14159,
1,
2,
3,
"wing",
"x",
"y",
"x"
})

90
doc/LPC/switch Normal file
View file

@ -0,0 +1,90 @@
NAME
switch
SYNTAX
switch (expr) block;
DESCRIPTION
Branch to the case label in statement that matches expr.
If no matching case label is found (by value or by type),
branch to the default label in statement.
A case label has the form
case expr_n :
where expr_n must be constant, or the form
case expr_n1 .. expr_n2 :
where expr_n1 and expr_n2 must be numeric constants and
expr_n1 < expr_n2.
Either all case labels have to be strings or all have to be
numeric. Only 0 is special: it is allowed in a switch
statement where all other labels are strings.
A default label has the form
default :
The default label defaults to the end of statement if not
given explicitly.
Whenever a 'break' statement is executed inside 'statement' a
branch to the end of the switch statement is performed.
EXAMPLE
Typical usage:
switch(random(100)) {
case 0 .. 22 : write("Nothing happens"); break;
case 23 .. 27 :
write("You are surrounded by a golden glow");
this_player()->heal_self(random(3));
break;
case 28 .. 32 :
write("The water was poisoned!\n");
this_player()->add_exp(this_player()->hit_player(random(4)));
break;
case 33 : write("You hear a voice whispering: "+random_hint());
/* fall through */
case 34 :
write("While you didn't paid attention, a water demon snatches\n"
"a coin out of your purse!\n");
this_player()->add_money(-1);
break;
default : write "You hear some strange noises\n"; break;
case 42 : return;
case 99 : write("It tastes good.\n";
}
BUGS
In C, the grammar for switch() is
switch (expr) block
allowing constructs like
switch (expr)
while (expr2)
{
case 1: ...
case 2: ...
}
The LPC compiler currently can't handle neither statements as switch
body, nor case labels embedded in inner loops.
HISTORY
LDMud 3.2.9/3.3 allowed to pass values of the wrong type to switch(),
the driver would in that case use the default case. Before, values
of the wrong type caused a runtime error.
LDMud 3.2.10 constrained the grammar to require a block for the
switch() body, not just a statement. This differs from the C
syntax, but was necessary as the compiler didn't handle
the statement case correctly.
SEE ALSO
for(LPC), foreach(LPC), do-while(LPC), if(LPC), while(LPC)

134
doc/LPC/types Normal file
View file

@ -0,0 +1,134 @@
CONCEPT
types
DESCRIPTION
Variables can have the following types:
o int An integer. Typically full 32 bits signed, yielding a
range of at least -2,147,483,648 to 2,147,483,647. The
exact available range is given by the predefined
macros __INT_MIN__ and __INT_MAX__.
Integer values can be specified in decimal, in
sedecimal when preceeded by '0x' (e.g. 0x11), binary
when preceeded by '0b' (e.g. 0b00010001), octal when
preceeded by '0o' (e.g. 0o21) and as character
yielding the charset value for the character as the number
to use (e.g. '0' yields 48 on ASCII machines).
Character values are enclosed in single-quotes ('),
with the sequence ''' returning the single-quote
itself. Instead of the literal character an
escape-sequence can be written between the
single-quotes:
\N : the character code N in decimal
\0xN : the character code N in sedecimal
\xN : the character code N in sedecimal
\0oN : the character code N in octal
\0bN : the character code N in binary
\a : BEL (0x07)
\b : Backspace (0x08)
\t : Tab (0x09)
\e : Escape (0x1b)
\n : Newline (0x0a)
\f : Formfeed (0x0c)
\r : Carriage Return (0x0d)
\<other character>: the given character
o status OUTDATED - status was planned to be an optimized
boolean format, but this was never actually
implemented. status does work; however, since it
is only an alias for type 'int', just use int.
o string Strings in lpc are true strings, not arrays of characters
as in C (and not pointers to strings). Strings are
mutable -- that is, the contents of a string can be
modified as needed.
The text of a string is written between double-quotes
("). A string can written over several lines when the
lineends are escaped (like a macro), however a better
solution is to write one string per line and let the
gamedriver concatenate them.
String text typically consists of literal characters,
but escape-sequences can be used instead of
characters:
\<CR> : Carriage Return (0x0d)
\<CR><LF> : ignored
\<LF> : ignored
\<LF><CR> : ignored
\N : the character code N in decimal
\0xN : the character code N in sedecimal
\xN : the character code N in sedecimal
\0oN : the character code N in octal
\0bN : the character code N in binary
\a : BEL (0x07)
\b : Backspace (0x08)
\t : Tab (0x09)
\e : Escape (0x1b)
\n : Newline (0x0a)
\f : Formfeed (0x0c)
\r : Carriage Return (0x0d)
\" : The double quote (")
\<other character>: the given character
Adjacent string literals are automatically
concatenated by the driver when the LPC program is
compiled. String literals joined with '+' are
concatenated by the LPC compiler if the #pragma
combine_strings is set (the default); if the pragma is
not set, the string literals are conacatenated at run
time.
o object Pointer to an object. Objects are always passed by
reference.
o array Pointer to a vector of values, which could also
be an alist. Arrays take the form ({ n1, n2, n3 })
and may contain any type or a mix of types. Arrays
are always passed by reference. Note that the size
of arrays in LPC, unlike most programming languages,
CAN be changed at run-time.
o mapping An 'associative array' consisting of values indexed by
keys. The indices can be any kind of datatype.
Mappings take the form ([ key1: value1, key2: value2 ]).
By default, mappings are passed by reference.
o closure References to executable code, both to local
functions, efuns and to functions compiled at
run-time ("lambda closures").
o symbol Identifier names, which in essence are quoted strings.
They are used to compute lambda closures, e.g. instead
of ({..., 'ident, ... }) you can write declare a
'symbol' variable foo, compute a value for it, and then
create the closure as ({ ..., foo, ... })
o float A floating point number in the absolute range
__FLOAT_MIN__ to __FLOAT_MAX__ (typically 1e-38 to 1e+38).
Floating point numbers are signified by a '.'
appearing, e.g. '1' is integer 1, but '1.' is
floating-point 1 .
o mixed A variable allowed to take a value of any type (int,
string, object, array, mapping, float or closure).
All uninitialized variables have the value 0.
The type of a variable is really only for documentation. Unless
you define #pragma strict_types, variables can actually be of
any type and has no effect at all on the program. However, it's
extremely bad style to declare one type but use another, so
please try to avoid this.
A pointer to a destructed object will always have the value 0.
SEE ALSO
alists(LPC), arrays(LPC), mappings(LPC), closures(LPC),
typeof(E), get_type_info(E), inheritance(LPC), pragma(LPC),
modifiers(LPC), escape(LPC)

72
doc/LPC/varargs Normal file
View file

@ -0,0 +1,72 @@
CONCEPT
varargs
DESCRIPTION
A function uses "varargs", short for "variable arguments", if
it intentionally may be called with less or more arguments
than formally specified.
The proper order to define a function call is:
[ modifier ] [ varargs ] [ return type ] function( args...)
Any other order will result in an error.
Given a function definition like this:
void fun (string arg1, int arg2, int arg3)
fun() has to be called with exactly three parameters: one
string and two integers.
If the function is defined as
varargs void fun (string arg1, int arg2, int arg3)
it is possible to call the function with just arg1, or arg1
and arg2. The remaining unspecified arguments (arg2 and arg3,
resp. arg3) are in these cases assumed to be 0.
To pass more arguments than specified, the functions last
parameter must be defined as following:
void fun (string arg1, int arg2, varargs int * arg3)
This allows fun() to be called with three or more arguments.
The arguments, except those assigned to the other parameters,
in this case arg1 and arg2, and collected into an array which
is then passed as arg3. For example
fun("foo", 1) -> arg3 == ({ })
fun("foo", 1, 2) -> arg3 == ({ 2 })
fun("foo", 1, 2, 3) -> arg3 == ({ 2, 3 })
The type of the varargs argument has to be an array of the
expected type (int*, object*, string*, ...); in this case,
only integers are allowed. To accept arguments of any kind,
define the parameter as 'varargs mixed' or 'varargs mixed*'.
To 'flatten' the received argument array in your own function
calls, use the efun apply(); e.g.:
apply(#'call_out, 1, arg3)
The two varargs variants can of course be combined:
varargs void fun (string arg1, int arg2, varargs int* arg3)
defines a function which may be called with any number of
arguments.
HISTORY
The possibility to pass more arguments than formally specified
was introduced in 3.2.1@132. Before, the excess arguments were
silently ignored.
SEE ALSO
pragma(LPC), apply(E), modifiers(LPC)

15
doc/LPC/while Normal file
View file

@ -0,0 +1,15 @@
NAME
while
SYNTAX
while (expr) statement;
DESCRIPTION
While 'expr' evaluates to non 0, execute statement.
A 'break' in the 'statement' will terminate the loop. A
'continue' will continue the execution from the beginning of
the loop.
SEE ALSO
for(LPC), foreach(LPC), do-while(LPC), if(LPC), switch(LPC)

18
doc/Makefile Normal file
View file

@ -0,0 +1,18 @@
SHELL=/bin/sh
MAKE=make
TAR=gtar
TAROPTS=cvzf
TARDEST=/usr/local/tar/games/lpmud/driver-docs-3.2.1@133-
#TARDATE=19-Sep-94
TARDATE=`date +%y-%m-%d`
TARSUFFIX=.tar.gz
TARFILES=doc/Makefile doc/README.driverdocs doc/TODO doc/man2tex.awk \
doc/3.2vs3.2.1 doc/LPC doc/applied doc/concepts doc/driver \
doc/efun doc/master
default:
@echo make tar
tar:
$(TAR) $(TAROPTS) $(TARDEST)$(TARDATE)$(TARSUFFIX) $(TARFILES)

89
doc/README Normal file
View file

@ -0,0 +1,89 @@
NAME
doc/README
DESCRIPTION
These man pages are organised in chapters, that are stored in
separate directories. A reference to foo(X) means the function
foo as descibed in chapter X, where X is one of:
A for applied/ member functions (lfuns) that the driver applies
to objects
C for concepts/ general concepts of LPMUD and LPC
D for driver/ some info about internal operation of the driver
E for efun/ the ``system calls'' of LPC
efun.de/ german translations of the efun/ manpages.
LPC for LPC/ about parts of the LPC language
M for master/ lfuns that are applied to the master object only
This collection does not contain anything about mudlib specific
object classes or lfuns, and also not about any simulated
efuns (from the simul_efun object), because all these are not
specific for the driver but for the particular installation.
Man-pages for the functions in the installation's simul_efun
object could go e.g. into the chapter SE (directory sefun/),
man pages for the standard objects (base classes) of the mudlib
would e.g. fit into chapter S (directory std/).
The man pages generally adhere to the following format, which
was inspired by the Unix man pages.
<manpage> ::= <section>+
There are one or more sections in a man page.
<section> ::= <section-name><newline><section-text><newline>
The section names are always at the beginning of a line, and
in all capital letters (debatable uglyness).
<section-name> ::= 'NAME' | 'CONCEPT' | 'SYNOPSIS'
| 'LAST UPDATED'
| 'SYNTAX' | 'DESCRIPTION' | 'AUTHOR' | 'BUGS'
| 'CAVEATS' | 'WARNING' | 'NOTE' | 'NOTES'
| 'EXAMPLE' | 'EXAMPLES' | 'HISTORY'
| 'SEE ALSO'
If a NAME or CONCEPT section occurs, it should give the name of
the man page.
If a SYNOPSIS section occurs, the name of the man page can be
derived from the function name, that appears in the line after
SYNOPSIS, which looks as follows:
<returntype><space>['*']<function-name>'('<parameter-prototypes>
SEE ALSO is followed by one or more lines that contain
crossreferences of the form
<crossreferences> ::=
<crossref>[','<white-space><crossref>]*<white-space><EOF>
<crossref> ::= <man-page-name>'('<chapter-abbrev>')'
The names of the referenced man pages are followed in brackets
by an abbreveiation for the chaper (i.e. directory) the man
page is in. The abbrevs are
If you want to process these man pages by some converter
program, you can use these to map the chapter names to file
names or whatever. There should be some sample awk scripts or
the like near this README file.
HISTORY
Most of the documentation is based on the 'driver-docs' collected
by Stefan 'Pepel' Petri up to Amylaar LPMud 3.2.1@141 and 3.2@316.
It was collectect from the original 2.4.5 docs, from the installed
docs of Nightfall, TubMud and MorgenGrauen.
Collection, correction and completion was done by Pepel@Nightfall,
with assistance and support by the LPC wizards of Nightfall,
TubMud, MorgenGrauen and TAPPMud, namely Hyp, Macbeth,
Mateese, and Marcus. Also to mention are Bumblebee, Boing,
Deepthought, Demos, Hate, Jof, Symmetry@TMI2.
The documentation was integrated into the driver distribution
in LDMud 3.2.6.
SEE ALSO
efun(E), applied(A), concepts(C), lpc(LPC), master(M), driver(D)

1
doc/TODO Normal file
View file

@ -0,0 +1 @@
The concepts/ LPC/ and driver/ still need to be worked at.

12
doc/applied/__INIT Normal file
View file

@ -0,0 +1,12 @@
SYNOPSIS
__INIT
DESCRIPTION
This function is constructed automagically by the parser at
compiler, if the parser was compiled with #define
INITIALISATION__INIT. This function is not intended to be
defined by the lpc objects, and never to be called from lpc
objects. This man page is here just for completeness.
SEE ALSO
initialisation(LPC)

20
doc/applied/add_weight Normal file
View file

@ -0,0 +1,20 @@
SYNOPSIS
int add_weight(int w)
DESCRIPTION
In compat mode, this function is used by the efun transfer().
An object that can contain other objects and is not a room
must define this function. It is called with the extra weight
of the object that is moved into it. If this is ok, then it
has to increment the local weight count, and return true.
Otherwise, return false, and the new object can not be entered
into this object.
The function is also called with the negative weight in the
object that the moving leaves.
Note that no set_weight() is required by the parser.
SEE ALSO
transfer(E), query_weight(A), prevent_insert(A)

12
doc/applied/applied Normal file
View file

@ -0,0 +1,12 @@
NAME
applied
DESCRIPTION
This directory contains descriptions for the lfuns used by
Amylaar's version of the LPC parser.
These are functions that are applied by the parser to the LPC
objects on various occasions.
SEE ALSO
efun(E), master(M), concepts(C), lpc(LPC), driver(D)

View file

@ -0,0 +1,16 @@
SYNOPSIS
int can_put_and_get()
DESCRIPTION
In compat mode, this function is used by the efun transfer().
Define this function in objects that are neither living nor
rooms if you want to make it possible to put something into
current object.
Return true if ok, otherwise 0. That means that default is
that it is not possible to put something into an object.
SEE ALSO
transfer(E), prevent_insert(A)

17
doc/applied/catch_msg Normal file
View file

@ -0,0 +1,17 @@
SYNOPSIS
void catch_msg(mixed *arr, object obj)
DESCRIPTION
When say(), tell_room() or tell_object() are used with an
array as message, the array will be passed to catch_message()
in all living objects that can hear it, instead of writing to
the user resp. sending to catch_tell(). This can be used to
implement communication protocols between livings. The second
denotes the object that has sent the message.
HISTORY
LDMud 3.2.11 added tell_object() to the efuns calling this
lfun for symmetry reasons.
SEE ALSO
say(E), tell_object(E), tell_room(E), catch_tell(A)

21
doc/applied/catch_tell Normal file
View file

@ -0,0 +1,21 @@
SYNOPSIS
void catch_tell(string)
DESCRIPTION
When a message is sent to an non-interactive object, via say(),
tell_object, tell_room() or write(), it will get to the function
catch_tell(string). The idea is to enable communications between
NPCs and from a user to an NPC.
Messages sent to an interactive object are also passed to that
object's catch_tell() lfun, _if it has one_. If the receiver
(or one of its shadows) doesn't have that lfun, the text is sent
to the socket directly. Only messages sent by an interactive
object to itself is always written to the socket immediately.
This allows to filter and process text before it is written
to a player.
SEE ALSO
enable_commands(E), say(E), tell_object(E), tell_room(E),
write(E), catch_msg(A)

51
doc/applied/clean_up Normal file
View file

@ -0,0 +1,51 @@
SYNOPSIS
int <lfun> (int refcount)
int <closure>(int ref, object ob)
DESCRIPTION
The hook H_CLEAN_UP defines a lfun or a closure used to clean
up an object. In older drivers this was hardwired to the lfun
clean_up().
The function is applied by the driver when an object hasn't been
used for a long time, to give it a possibility to
self-destruct. The refcount <ref> passed as argument will be 0 for
clone objects, 1 for a simple loaded object, and greater when
the object is cloned or inherited by some existing object. It
is recommended not to self_destruct the object when the
reference count is greater than one.
By convention, a refcount < 0 is used if some other object
asks the called object to clean_up.
If the function is a closure, the second argument <ob> is the
object to clean up.
If the hook specifies a non-existing lfun, or if the call
returns 0, no further attempt to clean up this object will be done.
Returning a non-zero value is only recommended when the reason
why the object can't self-destruct is likely to vanish without
the object being touched, that is, when no local function is
called in it, (and in compat mode also when the object is not
being moved around).
A typical mud configuration defines the time to wait for
clean_up() so long that you can assert reset() has been
called since the object has been touched last time.
EXAMPLES
A clone of /std/drink defines clean_up() to self-destruct if
it is empty, not carried a living being and not touched for
a long time.
A room that inherits /std/room defines clean_up() to
self-destruct if it is neither inherited nor used as a
blueprint, is empty and was not entered for a long time.
HISTORY
Before 3.2.1, the function was hardwired to the lfun clean_up().
SEE ALSO
reset(A), heart_beat(A), call_out(E), destruct(E), remove(A),
hooks(C)

24
doc/applied/create Normal file
View file

@ -0,0 +1,24 @@
SYNOPSIS
void create()
DESCRIPTION
In native mode, this function will be called only once on
creation of the object (this is when the object will be loaded
or cloned). Inside this function all major initialization can
be done. The current user and the previous object are
defined but the object has no environment.
EXAMPLE
object cloner;
void create() {
cloner=this_player();
}
Initialize the global variable to hold the one who
created/cloned the object.
For 3.2.1, the mudlib may be programmed to call other lfuns
than create() on an objects creation.
SEE ALSO
reset(A), init(A), __INIT(A), initialisation(LPC), hooks(C), native(C)

21
doc/applied/drop Normal file
View file

@ -0,0 +1,21 @@
SYNOPSIS
int drop(void)
int drop(int silently)
DESCRIPTION
In compat mode this lfun is used by the efun transfer().
It is called to check if an object wants to be moved out of
the inventory of a living object. drop() should return 1 to
prevent dropping. This is the opposite of the get() function.
That is because if drop() is not defined, it will always be
possible to drop an object.
If the object self-destructs when drop() is called, be sure to
return 1, as the destructed item surely not can be dropped.
Most compat mode LPC libraries to define one argument for
drop. If silently is true, no messages should be written.
SEE ALSO
transfer(E)

18
doc/applied/exit Normal file
View file

@ -0,0 +1,18 @@
SYNOPSIS
void exit(object ob)
DESCRIPTION
This function is used in compat mode only.
This function is called in the old environment everytime a
living object ob leaves it.
The function this_player() will return a random value, don't
use it at this point.
WARNING: Using this function is EXTREMELY dangerous. A single
bug, and you are forever (i.e. until the next reboot occurs)
caught in the room.
SEE ALSO
init(A)

14
doc/applied/get Normal file
View file

@ -0,0 +1,14 @@
SYNOPSIS
int get()
DESRIPTION
In compat mode, this function is used by the efun transfer().
If an object wants control over when it is possible to pick it
up, i.e. moved into a living object, then it must define
get(), and return 1 if ok to pick up.
id() has been called before this to identify the object.
SEE ALSO
transfer(E), drop(A)

40
doc/applied/heart_beat Normal file
View file

@ -0,0 +1,40 @@
SYNOPSIS
void heart_beat()
DESCRIPTION
This function will be called automatically every 2 seconds.
The start and stop of heart beat is controlled by the efun
set_heart_beat(). Be careful not to have objects with heart
beat running all the time, as it uses a lot of driver
resources. If there is an error in the heart beat routine, the
heart beat will be turned off for this object and the
master function heart_beat_error() is called. If the call
to the master function returns a non-zero value, the heart beat
will be turned back on again.
The function this_player() will return this object, but only if
it is living. Otherwise, this_player() will return 0.
The function will be called only if there is at least one interactive
user in the game.
Note that error messages will be given to the current user
which will be the object itself or nobody.
EXAMPLE
object owner;
void create() {
...
owner=this_player();
set_heart_beat(1);
}
void heart_beat() {
tell_object(owner, "Your heart goes: BUM BUM\n");
}
We have to use tell_object() because write goes to the current
user and this can only be the object itself or nobody.
SEE ALSO
set_heart_beat(E), call_out(E), enable_commands(E),
heart_beat_error(M)

15
doc/applied/id Normal file
View file

@ -0,0 +1,15 @@
SYNOPSIS
int id(string str)
DESCRIPTION
Let the object identify itself. If str matches an id of the
current object then return a none zero value.
This lfun is applied for the efun present().
EXAMPLE
int id(string str) {
return "sword" == str || "sword of fire" == str;
}
SEE ALSO
present(E)

56
doc/applied/init Normal file
View file

@ -0,0 +1,56 @@
SYNOPSIS
void init()
DESCRIPTION
The main purpose of this function is to set up the
add_actions() of an object. Therefore init() will always be
called when an object moves near another object and one or
both of them are marked as living.
The way init() will be invoked is determined in the following
manner:
If the object O that moves is marked as living then first call
init() of the destination object D with this_player() set to O.
Then apply the two following rules for each object C inside D:
If C is marked as living then call O->init() with
this_player() set to C.
If O is marked as living then call C->init() with
this_player() set to O.
Finally, if D is marked as living then call O->init(), with
this_player() set to D.
With 3.2.1, the actual move handling is part of the object
library, so a given installation may implement any other
scheme of calling init().
EXAMPLE
(This example assumes a traditional implementation of the
movement handling)
Lets say we have a object structure of living (l1 and l2) and
non living objects (n1 and n2) as the following:
l1
n1
l2
n2
If we now move another living object l3 into l1, the call
suequence of the init() functions looks like this:
l1->init() first init() of the destination will be called
n1->init() now iterate throw the inventory of the destination
l3->init()
l2->init()
n2->init()
l3->init() and finally call init() of the object that has
been moved
SEE ALSO
add_action(E), set_environment(E), environment(E), move_object(E),
hooks(C)

21
doc/applied/logon Normal file
View file

@ -0,0 +1,21 @@
SYNOPSIS
int logon (void)
int logon (int flag)
DESCRIPTION
When the driver created a new connection (either by accepting
it or by creating it with net_connect()) and bound it to an
object, it then calls logon() in that object.
The method should return 0 on failure, and everything else on
success.
If the driver attempted to create a connection in the
background and failed, it will call logon(-1) in the intended
object to inform the mudlib about the failure.
HISTORY
LDMud 3.2.10 added the extended meaning for net_connect().
SEE ALSO
connect(M), net_connect(E), exec(E)

View file

@ -0,0 +1,27 @@
SYNOPSIS
int|string modify_command(string cmd)
DESCRIPTION
After set_modify_command(mob) was called for an interactive
object iob, all commands for that user will be passed to
mob->modify_command(), and the return will then be checked for
actions.
If the result is a string, it is the new command to execute
instead of the given one. Note that it is not possible to
make several commands from one this way!
If the result is a non-zero number, the given command is to
be ignored. In case of the closure/lfun setting this may
mean that the closure/lfun already executed it.
If the result is 0, the originally given command is to be
used.
HISTORY
In 3.2.1@109 the name of the lfun to call must be specified
using the H_MODIFY_COMMAND_FNAME driver hook.
'modify_command' in the above text is therefore to read as
placeholder for the real name.
SEE ALSO
set_modify_command(E), hooks(C)

View file

@ -0,0 +1,13 @@
SYNOPSIS
string *parse_command_adjectiv_id_list(void)
DESCRIPTION
Used by parse_command().
EXAMPLE
string * parse_command_adjectiv_id_list() {
return ({ "iffish" });
}
SEE ALSO
parse_command(E)

View file

@ -0,0 +1,8 @@
SYNOPSIS
string *parse_command_id_list(void)
DESCRIPTION
Used by parse_command().
SEE ALSO
parse_command(E)

View file

@ -0,0 +1,8 @@
SYNOPSIS
string *parse_command_plural_id_list(void)
DESCRIPTION
Used by parse_command().
SEE ALSO
parse_command(E)

View file

@ -0,0 +1,15 @@
SYNOPSIS
int prevent_insert()
DESCRIPTION
In compat mode, this function is used by the efun transfer().
Define this function in objects that are neither living nor
rooms if you want to prevent to put something into current
object.
Return true if ok, otherwise 0. That means that default is
that it is not possible to put something into an object.
SEE ALSO
transfer(E), can_put_and_get(A)

9
doc/applied/query_weight Normal file
View file

@ -0,0 +1,9 @@
SYNOPSIS
int query_weight(void)
DESCRIPTION
In compat mode this lfun is used by the efun transfer().
Not that no set_weight() is required by the parser.
SEE ALSO
transfer(E), add_weight(A)

21
doc/applied/remove Normal file
View file

@ -0,0 +1,21 @@
SYNOPSIS
int remove(void)
DESCRIPTION
remove() does some housekeeping to ensure consistency and then
destruct the current object.
This lfun is not applied by the parser, but by other objects
to tell the current object to self-destruct. remove() should
be supplied by the base classes of the library. Return 1 if
actually self-destructed, 0 otherwise.
An alternative way to ensure the housekeeping on destruction
is through the use of the master apply prepare_destruct().
NOTE
Your actual mudlib may name this lfun differently, "remove" is
just the traditional name.
SEE ALSO
destruct(E), prepare_destruct(M)

32
doc/applied/reset Normal file
View file

@ -0,0 +1,32 @@
SYNOPSIS
void reset(int arg) /* compat mode */
void reset(void) /* native mode */
DESCRIPTION
In compat mode, reset() is called with arg 0 after the object
was compiled, and with arg != 0 every once in a while. In
native mode, create() is called after compiling, and reset()
is called without arguments every once in a while.
So, if the argument is zero, the parser is running in compat
mode, and reset() may call your create() code. If create() is
called, you're on the new version and reset() is not called
for object creation. You may call reset() from create() and
vice versa, but make sure they do not call each other
recursive on either type of driver.
reset() will be called only in objects that have been used
since the last call to reset(), i.e. a function in them was
called (other than reset() or clean_up()), or it was moved
around.
This function can be used to reset the state of the object or
do some checks or what ever. The game wouldn't be fun if no
challenges remained.
For 3.2.1, the mudlib can be programmed to call an other lfun
than reset() to reset an object.
SEE ALSO
clean_up(A), heart_beat(A), call_out(E), create(A), __INIT(A),
hooks(C), initialisation(M), native(C)

9
doc/concepts/concepts Normal file
View file

@ -0,0 +1,9 @@
NAME
concepts
DESCRIPTION
This directory contains man pages about basic concepts of the
LPC language as it is provided by Amylaars parser/interpreter.
SEE ALSO
driver(D), efun(E), applied(A), master(M), lpc(LPC)

559
doc/concepts/erq Normal file
View file

@ -0,0 +1,559 @@
CONCEPT
erq - External Request Demon
DESCRIPTION
Up to version 3.2.1@61, LPMud utilized two external programs
in an ad-hoc manner to solve problems: the 'hname' program to
resolve IP addresses into meaningful hostnames, and the
'indent' program to properly indent LPC files.
In version 3.2.1@61 both functions were united in a
generalized 'erq' process, to which additional functions may
be attached. Unfortunately it was never documented by Amylaar,
so the information presented here had to be reverse engineered
from the sources - better take it with a grain of salt.
The erq feature is available if the driver is compiled with
ERQ_DEMON defined (in config.h).
When the driver starts up, it tries to fork off the program
'BINDIR/erq --forked <other args>' (with BINDIR defined in
the Makefile). If this succeeds, the erq may talk with
the driver through stdin and stdout (piped through AF_UNIX
sockets). The erq has to signal its successfull start by
writing the character '1' back to the driver.
The erq has to understand these commandline arguments:
--forked: explained above
--execdir <dir>: The directory where the callable executables
can be found. If not specified, ERQ_DIR is used.
<dir> must not end in a '/' and should be absolute.
At runtime, the erq may be changed/removed from within the
mudlib using the efun attach_erq_demon(). This efun is given
an interactive object as argument, and takes the connection
away(!) from this object and stores it as the erq connection
to use (an old erq connection is closed first). The object
(which now no longer is interactive) is then no longer needed,
but may continue to exist.
The erq attached this way of course has to use the sockets it
opened to communicate with the driver.
Most of the communication between erq and driver is going to
be initiated by the driver (the erq has to look up the
hostnames for given IP addresses), but using the efun
send_erq() the mudlib may talk with the erq as well.
The communication between driver and erq is done using
messages of specified structures and constants (defined in
util/erq.h resp. sys/erq.h). The 'int32's are signed integers
of four byte length, and are sent with the MSByte first.
Every message must be sent atomically!
The head of the messages is always the same:
struct erq_msghead {
int32 msglen; /* Total size of message in bytes */
int32 handle; /* Identification number */
}
The 'handle' number is set by the driver (do not make
assumptions about its value) and is used to associated the erq
responses with the pending requests. This way the erq is free
to respond in an order different to those of the incoming
requests.
The messages send to the erq follow this symbolic format:
struct to_erq_msg {
int32 msglen;
int32 handle;
char request;
char data[0];
}
The 'request' denotes which service is requested from the erq,
the size and content of 'data' depends on the requested
service.
The answer message from the erq to the driver (if there is one
at all) may have two forms:
struct from_erq_msg {
int32 msglen;
int32 handle;
char data[0];
}
struct from_erq_keep_msg {
int32 msglen;
const int32 keep = ERQ_KEEP_HANDLE;
int32 handle;
char data[0];
}
The replied data from the erq is stored in 'data', which size
and content depends on the request answered. The answer is
identified by 'header.handle'. Normally, one request results
in just one response sent by the erq using struct from_erq_msg,
so the handle is recycled after this response.
Shall the erq send several responses (or break one response
into several parts), the struct from_erq_keep_msg has to be
used for all but the last response - this message with its
included special handle keeps the real handle alive.
Mudlib generated erq-calls specify the 'request' and the
'data' to be sent, and receive the 'data' replied. When
dealing with spawned programs, the first byte of the returned
'data' determines the content type of the received message.
The actual 'data' which the lpc programs get to see is sent
and retrieved as arrays of byte integers (integers in the
range of 0..255).
The actual interface between erq demon and driver is limited
to the general message formats and the hostname lookup
mechanism. The driver is meant to withstand erq demon failures
at least in a garbage-in garbage-out fashion. You could add
new requests to the erq demon, or write your own from scratch,
without changing the driver.
Currently five services are predefined in the supplied
erq-demon (util/erq.c in the driver source archive): looking
up a hostname, execution, forking or spawning an external
program, authentification of a connection, and handling of
external UDP/TCP connections. As mentioned above, only the
hostname-lookup is a true must.
For a program to be executable for erq, it must be placed in
or below ERQ_DIR (defined in config.h). On most unix systems,
it is possible to use a symlink instead of the whole program
if you want a standard binary. You could even symlink entire
directories like /usr/sbin, but chances are you make a big
security hole this way :-)
Hostname lookup:
request : ERQ_RLOOKUP
data sent: struct in_addr.s_addr addr // the address to resolve
data recv: struct in_addr.s_addr addr // the resolved address
char[] name // the hostname (if any)
If the sent address can't be resolved, just the address is
to be returned. The string need not be 0-terminated.
Hostname lookup:
request : ERQ_LOOKUP
data sent: char[] name // the name to resolve
data recv: struct in_addr.s_addr addr // the resolved address
If the sent address can't be resolved, no data is returned (the
driver will get a message with just the header).
Hostname lookup - IPv6:
request : ERQ_RLOOKUPV6
data sent: char[] addr // the address to resolve
data recv: char[] data // the resolved name
If the address could be resolved, the returned data is a string,
with exactly one space, in the form "<addr> <name>". <addr> is
the address passed to the erq, <name> is the hostname of the
address or, if there is no reverse-IPv6 entry for <addr>, the
IPv6 address which may or may not be different from <addr>.
If the address can not be resolved, the returned data is
an error message without a space (currently, just "invalid-format"
and "out-of-memory" are returned).
Execute/Fork program:
request : ERQ_EXECUTE/ERQ_FORK
data sent: char[] command // the command to execute
data recv: char status = CHILD_FREE
char rc // the success/error code
char info // additional information
The erq executes the sent command using the execv().
The erq does the processing of the command line arguments
(which must not contain '\') and checks the validity of the
command (it must not start with '/' nor contain '..'), which
is interpreted relative to ERQ_DIR.
The external program is executed from a fork()ed instance of
the erq, however, with ERQ_EXECUTE the erq waits until the
external program finished before replying its response, with
ERQ_FORK the response is immediately sent back.
Possible return codes are:
ERQ_OK : Operation succeeded.
ERQ_E_ARGLENGTH: Too long command.
ERQ_E_ARGFORMAT: Illegal argument given (contains '\');
ERQ_E_ARGNUMBER: Too much arguments (>= 96).
ERQ_E_ILLEGAL : Command from outside ERQ_DIR requested.
ERQ_E_PATHLEN : Commandpath too long.
ERQ_E_FORKFAIL : Command could not be forked;
info holds the errno value.
ERQ_EXECUTE features some more return codes:
ERQ_OK : Operation succeeded, <info> holds the exit status.
ERQ_SIGNALED : Command terminated the signal <info>.
ERQ_E_NOTFOUND : No process found to wait() for.
ERQ_E_UNKNOWN : Unknown exit condition from wait().
Spawn program:
request : ERQ_SPAWN
data sent: char[] command // the command to execute
data recv: Spawn failed:
char rc // the error code (see ERQ_FORK)
char info // additional information
data recv: Spawn succeeded:
char rc = ERQ_OK
char[] ticket // the spawn ticket.
The erq executes the sent command as if given an ERQ_FORK
command, but returns additional information about the
started process to allow further communication.
In contrast to ERQ_FORK, ERQ_SPAWNED processes may be
controlled via ERQ_KILL, receive data from the mud via
ERQ_SEND on their stdin, and output from their stdout/stderr
is sent back to the mud.
The spawned process is identified by its <ticket> (don't
make any assumptions about its length or content), the transaction
itself by <handle>.
Send data to spawned program:
request : ERQ_SEND
data sent: char[] ticket // the addressed process ticket.
char[] text // the text to send.
data recv: char rc // the success/error code.
int32 info // opt: additional info.
The <text> is sent to the stdin of the spawned process
identified by <ticket>.
Possible return codes are:
ERQ_OK : Operation succeeded, no <info> is replied.
ERQ_E_TICKET : The given ticket is invalid, no <info> replied.
ERQ_E_INCOMPLETE: Only <info> chars of the text have been
sent.
If a callback is specified, the erq will send
a ERQ_OK message once all data has been sent
(this may never happen).
ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
happened while sending the text.
ERQ_E_PIPE : Error E_PIPE (also stored in <info>)
happened while sending the text.
ERQ_E_UNKNOWN : The error with code <info> happened
while sending the data.
Amylaar-erq doesn't try to re-send the remaining data after
a ERQ_E_INCOMPLETE, so there will never be an ERQ_OK.
Send a signal to a spawned program:
request : ERQ_KILL
data sent: char[] ticket // the addressed process ticket
int32 signal // the signal to send
data recv: char rc // the success/error code
The <signal> is sent to the spawned process identified by <ticket>.
Possible return codes are:
ERQ_OK : Operation succeeded, no <info> is replied.
ERQ_E_TICKET : The given ticket is invalid, no <info> replied.
ERQ_E_ILLEGAL : The given signal is illegal.
Data replies from spawned programs:
data recv: char out_or_err // type of text output
char[] text // text output by child process
The child process controlled by the erq did output <text>
on stdout (<out_or_err> == ERQ_STDOUT) resp. on stderr
(<out_or_err> == ERQ_STDERR).
Exit notifications from spawned programs:
data recv: char rc // the exit code
char info // additional information.
The child process controlled by the erq did terminate.
Possible exit codes are:
ERQ_EXITED : Process exited with status <info>.
ERQ_SIGNALED : Process terminated by signal <info>.
ERQ_E_UNKNOWN : Process terminated for unknown reason.
Authentificate connection (see rfc 931):
request : ERQ_AUTH
data sent: struct sockaddr_in remote // the address to check
int32 port // the mud port
or
data sent: int32 remote_ip // remote ip to check
int16 remote_port // remote port to check
int16 local_port // the mud port
data recv: char[] reply // the data received by authd
The erq attempts to connect the authd on the remote system
and to verify the connection between the remote port and the
mud port. The latter will normally be the port number of the
socket on besides of the gamedriver, retrieveable by
query_ip_number().
The answer from the authd (one line of text) if there is any
is returned as result.
The second form of the ERQ_AUTH command is recognized by
the xerq as alternative.
Open an UPD port:
request : ERQ_OPEN_UDP
data sent: char[2] port // the port number to open (network order)
data recv: Open failed:
char rc // the success/error code.
char info // opt: additional info.
data recv: Open succeeded:
char rc = ERQ_OK
char[] ticket // the connection ticket.
The erq opens an UDP-port on the host machine with the given
port number.
Possible exit codes are:
ERQ_OK : Operation succeeded.
ERQ_E_ARGLENGTH : The port number given does not consist
of two bytes.
ERQ_E_NSLOTS : The max number of child processes (given
in <info>) is exhausted.
ERQ_E_UNKNOWN : Error <info> occured in one of the system
calls done to open the port.
Once the port is open, it is treated as if is just another
spawned program.
Send data over an UDP port:
request : ERQ_SEND
data sent: char[] ticket // the addressed port's ticket.
struct in_addr.s_addr addr // address of receiver.
struct addr.sin_port port // port of receiver.
char[] text // the text to send.
data recv: char rc // the success/error code.
int32 info // opt: additional info.
The <text> is sent from our port <ticket> to the network
address <addr>, port <port>.
Possible return codes are:
ERQ_OK : Operation succeeded, no <info> is replied.
ERQ_E_TICKET : The given ticket is invalid, no <info> replied.
ERQ_E_INCOMPLETE: Only <info> chars of the text have been
sent. The erq will send a ERQ_OK message
once all data has been sent.
ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
happened while sending the text.
ERQ_E_PIPE : Error E_PIPE (also stored in <info>)
happened while sending the text.
ERQ_E_UNKNOWN : The error with code <info> happened
while sending the data.
Close an UDP port:
request : ERQ_KILL
data sent: char[] ticket // the addressed port's ticket
int32 signal // the signal to send (ignored)
data recv: char rc = ERQ_OK
The port <ticket> is closed. The <signal> must be sent, but
its value is ignored.
Data received over an UDP connection:
data recv: char out_or_err = ERQ_STDOUT
struct in_addr.s_addr addr // ip-address of sender
struct addr.sin_port port // port of sender
char[] text // data received
The UPD port controlled by the erq did receive <text> over
the network from the sender at <addr>, reply port number <port>.
Open an TCP to listen for connections:
request : ERQ_LISTEN
data sent: struct addr.sin_port port // the port number to open
data recv: Open failed:
char rc // the success/error code.
char info // opt: additional info.
data recv: Open succeeded:
char rc = ERQ_OK
char[] ticket // the connection ticket.
The erq opens an TCP-port on the host machine with the given
port number to listen for connections.
Possible exit codes are:
ERQ_OK : Operation succeeded.
ERQ_E_ARGLENGTH : The port number given does not consist
of two bytes.
ERQ_E_NSLOTS : The max number of child processes (given
in <info>) is exhausted.
ERQ_E_UNKNOWN : Error <info> occured in one of the system
calls done to open the port.
Once the port is open, it is treated as if is just another
spawned program.
Open an TCP port:
request : ERQ_OPEN_TCP
data sent: struct in_addr.s_addr ip // the ip to address
struct addr.sin_port port // the port to address
data recv: Open failed:
char rc // the success/error code.
char info // opt: additional info.
data recv: Open succeeded:
char rc = ERQ_OK
char[] ticket // the connection ticket.
The erq opens an TCP-port on the host machine and tries to connect
it to the address <ip>:<port>.
Possible exit codes are:
ERQ_OK : Operation succeeded.
ERQ_E_ARGLENGTH : The port number given does not consist
of two bytes.
ERQ_E_NSLOTS : The max number of child processes (given
in <info>) is exhausted.
ERQ_E_UNKNOWN : Error <info> occured in one of the system
calls done to open the port.
Once the port is open, it is treated as if is just another
spawned program.
Send data over a TCP connection:
request : ERQ_SEND
data sent: char[] ticket // the addressed process ticket.
char[] text // the text to send.
data recv: char rc // the success/error code.
int32 info // opt: additional info.
The <text> is sent to the stdin of the spawned process
identified by <ticket>.
Possible return codes are:
ERQ_OK : Operation succeeded, no <info> is replied.
ERQ_E_TICKET : The given ticket is invalid, no <info> replied.
ERQ_E_INCOMPLETE: Only <info> chars of the text have been
sent. The erq will send a ERQ_OK message
once all data has been sent.
ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
happened while sending the text.
ERQ_E_PIPE : Error E_PIPE (also stored in <info>)
happened while sending the text.
ERQ_E_UNKNOWN : The error with code <info> happened
while sending the data.
Data ready to read on TCP connection:
data recv: char out_or_err = ERQ_OK
char[] ticket // ticket of this connection
There is data available to read on the specified TCP connection.
Data received over a TCP connection:
data recv: char out_or_err = ERQ_STDOUT
char[] text // data received
The TCP port controlled by the erq did receive <text>.
TCP connection closes on error:
data recv: char out_or_err = ERQ_E_UNKNOWN
char errno // errno from socket operation
The TCP connection caused an error <errno> and has been closed.
TCP connection closed:
data recv: char out_or_err = ERQ_EXITED
The TCP connection closed regularily (End Of File).
Connection pending on TCP socket:
data recv: char out_or_err = ERQ_STDOUT
The TCP 'listen' port controlled by the erq received
a connection request.
Accept a pending connections:
request : ERQ_ACCEPT
data sent: char[] ticket // the ticket of this socket
data recv: Accept failed:
char rc // the success/error code.
char info // opt: additional info.
data recv: Accept succeeded:
char rc = ERQ_OK
struct in_addr.s_addr ip // remote side's ip
struct addr.sin_port port // remote side's port
char[] ticket // the new ticket.
The erq accepts a new connection on an accept-TCP-port, creates
an child and ticket for it and returns its ticket together with
the remote's side <ip>:<port> number (in network byte order).
Possible exit codes are:
ERQ_OK : Operation succeeded.
ERQ_E_ARGLENGTH : The port number given does not consist
of two bytes.
ERQ_E_NSLOTS : The max number of child processes (given
in <info>) is exhausted.
ERQ_E_TICKET : the ticket didn't match
ERQ_E_UNKNOWN : Error <info> occured in one of the system
calls done to open the port.
Once the port is open, it is treated as if is just another
spawned program.
HISTORY
The erq was introduced with 3.2.1@61.
ERQ_AUTH was introduced with 3.2.1@81.
ERQ_SEND, ERQ_SPAWN, ERQ_KILL were introduced with 3.2.1@82.
ERQ_OPEN_UDP, ERQ_OPEN_TCP, ERQ_LIST were introduced with 3.2.1@98.
ERQ_RLOOKUPV6 was introduced in 3.2.8.
LDMud 3.2.9 added the '--execdir' argument to erq, and the ERQ_OK
after ERQ_E_INCOMPLETE protocol.
SEE ALSO
attach_erq_demon(E), send_erq(E), stale_erq(M), rfc 931
query_ip_number(E)

16
doc/concepts/files Normal file
View file

@ -0,0 +1,16 @@
CONCEPT
files
DESCRIPTION
As a wizard, you are working with files. Each file represents
the bulding plan for one or more objects (except text or doc files
of course).
The mudlib has a root, and when working with filenames, you
can always specify a full pathname from the root by starting
with a '/' (slash) at the beginning of the file name.
(oops, truncated - why when where did it happen?)
SEE ALSO
objects(C), create(A), reset(A)

505
doc/concepts/hooks Normal file
View file

@ -0,0 +1,505 @@
CONCEPT
driver hooks
DESCRIPTION
To allow a greater flexibility of the muds, the gamedrivers
since 3.2.1 moved several once hardcoded 'underground'
activities from the driver into the mudlib. This includes for
example the differences between compat and native mode.
The hooks are set with the privileged efun set_driver_hook().
Some of the hooks are mandatory, some not. Most hooks accept
unbound lambda closures as values, some also lfun closures or
even strings.
The hooks are identified by an ordinal number, for which
symbolic names are defined in /sys/driverhooks.h.
H_MOVE_OBJECT0
H_MOVE_OBJECT1
Mandatory hooks to implement the efun void move_object().
Hook setting must be an unbound lambda closure:
void <closure>(object item, object dest)
Upon call, the hook has to perform the move itself (by using
set_environment()) and all depending actions (like the calls to
init() to add actions).
The difference lies in the binding of the set hook prior to
the call: the H_MOVE_OBJECT0 closure is bound to the current
object, the H_MOVE_OBJECT1 to 'item'.
If both hooks are set, H_MOVE_OBJECT0 is ignored.
H_LOAD_UIDS
H_CLONE_UIDS
Mandatory hooks to determine the uid and euid of loaded or cloned
objects. Hook settings can be any closure:
mixed <load_uids closure> (string objectname)
mixed <clone_uids closure>(object blueprint, string objectname)
When an object is newly loaded, the H_LOAD_UIDS hook is
called with the object name as argument.
When an object is cloned, the H_CLONE_UIDS hook is called
with the blueprint object as first and the clone's designated
name as second argument.
In both cases the new object already exists, but has 0 uids.
For the result, the following possibilities exist (<num> is
a non-zero number, <no-string> is anything but a string):
"<uid>" -> uid = "<uid>", euid = "<uid>"
({ "<uid>", "<euid>" }) -> uid = "<uid>", euid = "<euid>"
({ "<uid>", <no-string> }) -> uid = "<uid>", euid = 0
If strict-euids is not active, the following results are
possible, too:
<num> -> uid = 0, euid = 0
({ <num>, "<euid>" }) -> uid = 0, euid = "<euid>"
({ <num>, <no-string> }) -> uid = 0, euid = 0
H_CREATE_SUPER
H_CREATE_OB
H_CREATE_CLONE
Optional hooks to initialize an object after creation.
Hook setting can be unbound lambda closures, or the name of
the function to call in the object.
H_CREATE_SUPER is called for blueprints implicitely loaded
by inheritance, H_CREATE_OB for explicitely loaded
blueprints/objects, and H_CREATE_CLONE for cloned objects.
If the hook is a closure expecting an argument, it is bound
to the current object and called as
int <closure> (object obj_to_init)
If the hook as a closure without arguments, it is bound to
the object to be initalized and called as
int <closure> ( void )
If the result of the call is a non-zero number, it is used
as the interval to wait before the first reset(), else the default
interval computed from TIME_TO_RESET is used.
If the hook is defined as the name of an lfun (static or public)
in the object, it is called in the object as
void <name>(0)
and any result is ignored.
In this call the previous_object() is the object initiating
the load.
H_RESET
Optional hook to reset an object.
Hook setting can be unbound lambda closures, or the name of
the function to call in the object.
This hook is called to reset the object after a certain time
since its creation/last reset.
If the hook is a closure, it is bound to the object to be
reset and called with no argument:
void|int <closure> ( void )
If the result of the call is a positive number, it is used
as the interval to wait before the next reset().
If the result is 0, the default interval computed from
TIME_TO_RESET is used.
If the result is a negative number, the object will not be
reset again, unless directed otherwise by set_next_reset().
If the hook is defined as the name of an lfun (static or public)
in the object, it is called in the object as
void <name>(1)
and any result is ignored.
In this call the previous_object() is the object initiating
the reset.
If the function does not exist, the object won't be reset
again.
H_CLEAN_UP
Optional hook to clean up an object.
Hook setting can be any closure, or the name of the function
to call in the object.
This hook is called for an object if it hasn't been used
for at least TIME_TO_CLEAN_UP seconds, to give it the
opportunity to self destruct.
If the hook is a closure, it is called as
int <closure>(int ref, object ob)
with the object's refcount as first argument, and object itself as
second. Lambda closures are also bound to the object prior to the
call.
If the hook is the name of an lfun, it is called in the
object with its refcount as argument:
void|int <name>(int ref)
In both calls, the refcount is constructed as:
ref = 0: the object is a clone, or a blueprint with
replaced program.
ref = 1: the object is a swapped or unused blueprint.
ref > 1: the object is a used blueprint with <ref> references.
The cleanup method has the possibility to destruct the
object. To survive this time, but try again some time later,
the call has to result in a non-zero value.
If the hook specifies a non-existing lfun, or if the call
returns 0, no further attempt to clean up this object will be done.
H_COMMAND
Optional hook to parse and execute commands. If this hook is used,
it bypasses the normal command parsing done by the driver (including
the MODIFY_COMMAND and NOTIFY_FAIL hooks).
The hook is called with two parameters: the command received
from the living (interactive user or NPC), and the living object
(the 'command giver') itself. The hook has to return non-0 if the
command was found and executed, and 0 otherwise.
At the time the hook is called, query_command() returns the command
string and this_player() returns the living object. query_verb() and
query_notify_fail() return 0.
If the hook is a string, it is the name of an lfun in the command
giver:
int <name>(string command, object command_giver)
If the hook is a closure, it is called as:
int <closure>(string command, object command_giver)
Lambda-closures are additionally bound to the command_giver.
H_MODIFY_COMMAND
Optional hook to modify commands (both entered or given by a
call to command()) before the parser sees them (this includes
special commands like 'status').
Hook setting can be any closure, the name of the function
to call in the object, or a mapping.
For interactives this hook is used only if the interactive
object has no command modifier already set by the efun
set_modify_command().
If the hook is a closure, it is called as
int|string <closure>(string cmd, object player)
with the entered command as first, and the command giving
player as second argument.
If the hook is a string, it is used as the name of an lfun
in the command giving player, which is called as
int|string <name>(string cmd)
If the hook is a mapping, it is queried with the given
command as index, and the data retrieved is used (defaults
to 0 if no data is stored for a given command). If an entry
is a closure, it is called as
int|string <closure>(string cmd, object player)
and the result from the call is used as 'the' result.
The result is treated equal in all three cases.
If the result is a string, it is the new command to execute
instead of the given one. Note that it is not possible to
make several commands from one this way!
If the result is a non-zero number, the given command is to
be ignored. In case of the closure/lfun setting this may
mean that the closure/lfun already executed it.
If the result is 0, the originally given command is to be
used.
It is possible for the hook to change the command giver
(this_player()) for the execution of the command. This means that
even though the commands are execute for the original commandgiver,
this_player() will return the changed commandgiver.
H_MODIFY_COMMAND_FNAME
Mandatory hook specifying the name of the 'modify_command'
function to call for newly entered commands as result of a
set_modify_command().
Hook setting must be a string.
If set_modify_command() is used for an interactive user, all
newly entered commands are first passed to the function
named by this hook.
The function is called as
int|string <name>(string cmd)
If the result is a string, it is the new command to execute
instead of the given one. Note that it is not possible to
make several commands from one this way!
If the result is a non-zero number, the given command is to
be ignored. In case of the closure/lfun setting this may
mean that the closure/lfun already executed it.
If the result is 0, the originally given command is to be
used.
It is possible for the hook to change the command giver
(this_player()) for the execution of the command. This means that
even though the commands are execute for the original commandgiver,
this_player() will return the changed commandgiver.
H_NOTIFY_FAIL
Mandatory hook to issue the default message if an entered
command couldn't be parsed and no notify_fail() command is
in effect.
Hook setting can be a any closure, or a string.
If set to a string, it is the message returned to the
player.
If set to a closure, it is called as
string <closure>(string entered_command, object cmd_giver)
and the result is used as failure message. Lambda closures
are bound to this_player() prior to execution.
<cmd_giver> is the object which received the command in
the first place. It is usually identical with this_player(),
unless the H_MODIFY_COMMAND hook changed it.
H_SEND_NOTIFY_FAIL
Optional hook to send the notify fail message, regardless
of how it was determined, to the player. If the hook is not
set, the message is delivered using tell_object() internally.
Hook setting can be a string or a closure.
If the hook is a string, it is the name of a (possibly static)
function to call in the current command giver. If the hook
is a closure, it is the function to be called. Lambda closures
are bound to the current command giver first.
The function is called as
void <function>(string msg, object msgobj, object orig_cmd_giver)
<msg> is the notify fail message to be delivered.
<msgobj> is the object which set the message. It is 0 for the
default message.
<orig_cmd_giver> is the object for which the original command
was first received. It is usually identical with the current
command giver this_player().
H_NO_IPC_SLOT
Optional hook specifying the message given to logins
rejected due to space limitations (MAX_PLAYER).
Hook setting has to be string.
If set to 0, the default message "Lpmud is full. Come back
later." is issued.
H_INCLUDE_DIRS
Semi-mandatory hook specifying the directories where <>-type
include files are searched (this includes ""-includes not
found as specified).
Hook setting may be any closure or a string array.
If not set, only ""-type includes may be used in LPC
programs.
The hook is called only if a call to master::include_file()
does not return a usable filename.
If the hook setting is a string array, it has to contain the
path names of those directories where <>-type includes are
to be searched. The directories are searched in the order
they appear in the array. The directory name and the name of
the actual include file are concatenated, therefore the
directory names have to end in '/'. Leading slashes may be
omitted.
If the setting is a closure, it is called as
string <closure>(string include_name, string current_file)
with the name of the desired include file as first, and the
name of the compiled LPC file as second argument.
Result has to be the complete path name of the include file
to use (leading slashes may be omitted).
If the closure is a lambda closure, it is bound to
this_object() prior to execution.
See also: master apply include_file()
H_AUTO_INCLUDE
Optional hook specifying a string to be included before
the source of every compiled LPC object.
Hook setting can be a string or a closure.
If the setting is a string, it will be automatically included before
the source of every compiled LPC object.
If the setting is a closure, it is called as
string <closure>(string base_file, string current_file
, int sys_include)
for every file opened by the compiler. <base_file> will be the
filename of the compiled object, <current_file> the name of a file
included directly or indirectly by the <base_file>. When the
<base_file> itself is opened, <current_file> will be 0. For an
included file, <sys_include> will be TRUE if it is a <>-type
include.
If the result is a string, it will be included before the actual
text of the file.
H_TELNET_NEG
Optional hook to specifiy how to perform a single telnet
negotiation.
Hook setting may be any closure or a string.
If not set, most telnet options are rejected (read: only a
very minimal negotiation takes place).
The hook is called whenever the driver receives a demand for
option negotiation for:
SB: Suboption negotiation
TELOPT_LINEMODE: linemode
TELOPT_NAWS: window size
TELOPT_TTYPE: terminal type
TELOPT_TM: timing mark
TELOPT_NEWENV: remote environment variables
TELOPT_ENVIRON: remote environment variables
TELOPT_XDISPLOC: remote X display address
TELOPT_TSPEED: terminal speed
TELOPT_BINARY: binary data, needed for non-ASCII charsets
TELOPT_EOR: TinyFugue prompt marker (together with EOR)
TELOPT_COMPRESS: Mud Compression Protocol
TELOPT_COMPRESS2: Mud Compression Protocol
TELOPT_MSP: Mud Sound Protocol
TELOPT_MXP: Mud Extension Protocol
The hook has then to perform the negotiation using the efun
binary_message().
Alternatively, if H_NOECHO is set, this hook is called
for all telnet data received.
If the setting is a string, it used as name of an lfun to
call in this_player():
void|mixed <name>(int action, int option [, int * opts ] )
Similar, if the setting is a closure, it is called as:
void|mixed <closure>(int action, int option [, int * opts ] )
with unbound lambda-closures being bound to this_player()
prior to execution.
The hook is called for a 'DO/DONT/WILL/WONT <opt>' with the action
(DO/DONT/...) as the first, and <opt> as the second argument.
If the driver receives the sequence
IAC SB <opt> <opts>...
followed by IAC SB/SE, the hook is called with 'SB' as first
argument, <opt> as second, and <opts> as an array of integers as
third argument.
H_NOECHO
Optional hook to specifiy how to perform the telnet actions
to switch the echo mode (used for e.g. password input_to()s).
Hook setting may be any closure or a string.
If not set, a default handling is performed.
If the setting is a string, it used as name of an lfun to
call in the intercative <user>:
void <name>(int flag, object user, int no_telnet)
where <flag> is the echo-flag passed to the input_to()
statement.
Similar, if the setting is a closure, it is called as:
void <closure>(int flag, object user, int no_telnet)
with unbound lambda-closures being bound to this_player()
prior to execution.
When set, the hook is called whenever the driver needs to
change the echo mode, thus you can negotiate about things
that are coupled with it, like LINEMODE or
character-at-a-time.
<local> is a boolean flag: it is TRUE when input_to() was
called with the INPUT_NO_TELNET flag.
IMPORTANT: If this hook is used, the control of all telnet
negotiation is transferred to the mudlib: all incoming negotiations
are passed to H_TELNET_NEG, and the sending of no-echo negotiations
is handled by this hook.
H_ERQ_STOP
Optional hook to notify the mudlib about the termination of
the erq demon.
Hook setting may be any closure.
The closure is called without arguments:
void <closure>()
and may do whatever it likes to clean-up after the erq.
HISTORY
H_NOTIFY_FAIL recevied the new 'command_giver' argument in 3.2.7.
The hooks concept was introduced in 3.2.1
The hook for moving was introduced in 3.2.1@1
The hook for clean up was introduced in 3.2.1@34
The hook for modifying commands was introduced in 3.2.1@51,
the evaluation of mapping as hooks was extended in 3.2.1@54.
The lfun called as result of set_modify_command() was
'hooked' in 3.2.1@109.
The hooks for notify_fail and full muds were introduced in 3.2.1@55.
The hook for include dirs was introduced in 3.2.1@57.
The hook for telnet negotiation was introduced in 3.2.1@60.
The hooks for no-echo negotiation and erq-lossage notification
were introduced in 3.2.1@85.
H_COMMAND was introduced in 3.2.7.
H_SEND_NOTIFY_FAIL and H_AUTO_INCLUDE were introduced in 3.2.9.
LDMud 3.2.10 allowed static functions to be specified by name
for H_RESET and H_CREATE_*
LDMud 3.2.11 added the <no_telnet> argument to H_NOECHO.
SEE ALSO
native(C), set_driver_hook(E)

63
doc/concepts/imp Normal file
View file

@ -0,0 +1,63 @@
CONCEPT
imp
LAST UPDATED
Deepthought, 10-Nov-92
Pepel, 18-Nov-93
DESRIPTION
This document describes IMP, the intermud message protocol,
also known as Intermud-1.
Imp messages are exchanged between muds using UDP
(unreliable datagram protocol) packets. Each mud provides
a connection endpoint which is given by the ip host address
and the UDP port number. Muds may then send messages to
this port by using the efun send_imp(). The applied function
receive_imp will be called by the driver in the master
object if a imp message arrives at the mud's UDP port.
Imp message packets have the following format:
password@objectname@functionname[[@argument]...]
<password> is the connection password to verify incoming
imp packets. It is encoded using crypt(E) and compared to
the stored password. Each mud participating in the imp
network has a secret password which is encoded by the
admin and distributed to remote muds with which the mud
should have direct connection. Encryted passwords may also
propagated to other muds over already secure channels.
<objectname> is a logical name which is not to be confused
with mudlib object filenames. It is used by receive_msg in
the master object to route the message to another object by
associating the logical object name with a real mudlib file
name. A good idea would be to reserve a special directory
for imp objects, e.g. /secure/net/<objectname>.
<functionname> is the function which is called by the master
object in the object described by <objectname>.
<argument> are additional arguments which are handed to the
function <functionname>. The exact definition of functions
and arguments are left to the imp applications.
AUTHOR
originally Deepthought
NOTE
The above is only particularly correct nowadays. Recently a
package name ``inetd'' was published, that is based on the IMP
mechanism in the driver (send_imp() and receive_imp()), but
it uses a different message format. That package seems to
enjoy much publicity and is installed in a number of muds. For
details look into the inetd description.
An other method of inter mud connection is the Mudlink
package, which uses a normal user connection that is connected
to a special user object, and an auxiliary process that does
the connection to other muds.
SEE ALSO
send_imp(E), receive_imp(M), intermud(C)

176
doc/concepts/inheritance Normal file
View file

@ -0,0 +1,176 @@
CONCEPT
Inheritance
DESCRIPTION
Have you noticed how many objects in the system have the same
functionality in common? Let's look at rooms for instance, they
all have the ability to host people and provide commands. It's
not that every room is programmed with the same basic functions
again and again, rather it will use a model room and then make
some special changes to it. That doesn't work by copying the
file.. Ouch! Don't replicate code! But by putting a tiny inherit
declaration
inherit "<model-class>";
at the beginning of your new file. This must come before any local
ariables or functions. Once inherited your class will behave just
like the model class, because all the public methods are available
to the outside world. Now it is in your hands to change such an
inherited behaviour. You have the following tools to do so:
* Access to variables
It is one of the best design decisions in LPC that variables
are not accessible from outside, but you can use inherited
variables just as if they were your own. Modifiers apply however.
* Method overloading
int method_that_also_exists_in_the_model() {
<your new code>
}
You can simply rewrite a method that is also defined in the model
class, and thus change how it behaves. Contrary to other languages
in LPC method overloading only matches the name of the method, so
even by changing the amount and type of parameters you will mask
out the original version of the method. You can even apply other
modifiers to it as the original.
* Calling inherited methods
int method_that_also_exists_in_the_model() {
<your new code>
return ::method_that_also_exists_in_the_model();
}
You can add to the behaviour of a method by redefining it,
then calling it from within your new version. You can actually
call inherited methods from anywhere in your code. The double
colon tells the compiler you are looking for the inherited
variant.
EXAMPLE
Let's imagine very simple food in a file called "/the/food.c":
// unless "modified" variables are accessible by inheritors
int vitamins = 10;
// please overload this function with your own description
public short() { return "something edible"; }
// let's do some standard action for food
public consume() {
this_player() -> nourish(vitamins);
destruct(this_object());
}
And now someone else decides to do some italian cooking in a
file called "/the/fusilli.c"
inherit "/the/food";
// we have our own variables.
int gone_cold = 0;
// and we simply redefine the short() function to replace it
public short() {
// description changes depending on gone_cold
return "a "+( gone_cold ? "stinking" : "steaming" )
+" plate of fusilli";
}
// we have a new function to make food go cold
private deteriorate() {
gone_cold = 1;
write("The fusilli have gone cold.\n");
}
// assume this gets called at creation
private create() {
// we can access the variable we inherited from food.c
vitamins = 44; // tomato has plenty of vitamins
// go cold in 5 minutes
call_out( #'deteriorate, 5 * 60 );
}
// we can overload the function even with new parameters
public consume(how) {
// fetch the name of the person, or use "Someone"
string name = this_player() -> name() || "Someone";
if (!gone_cold) {
write("You enjoy a delicious plate of fusilli.\n");
say(name +" guzzles a plate of hot fusilli.\n");
}
else if (how == "quickly") {
write("You eat the fusilli so quickly you "
"hardly notice they have gone cold.\n");
say(name +" wolfs down a plate of cold fusilli.\n");
}
else {
write("You eye the plate and wonder if you "
"really feel like eating cold fusilli.\n");
return; // don't eat
}
// and here comes the most important part:
// we execute consume() from food.c, so we
// actually inherit its behaviour.
::consume();
}
ADVANCED USAGE
* Doing multiple inheritance
While the Java(TM) language has so-called interfaces as a kludge,
LPC doesn't need them as it supports real multiple inheritance.
A very powerful feature, it lets you combine the behaviour of
several classes into a new one. Simply put several lines of
inherit declarations underneath each other. If you have name
collisions in the namespace of inherited methods, you will have
to address them explicitely with a "the/file"::method(args) syntax.
* Wildcarded multiple inheritance
LDMUD 3.2.1@117 introduces an advanced voodoo syntax which allows
you to call several methods in model classes at once, but for some
technical reasons it cannot pass any arguments. This works by
writing a glob type match ('*' and '?' wildcards) into the string
in front of the double colon, as in "*"::create(). I wouldn't
recommend you to use this, it's better to be clearly conscious of
what you inherit and do. But if you're desperate, there you go.
ADVANCED EXAMPLE
inherit "foo";
inherit "bar";
inherit "baz";
inherit "ball";
reset() {
"ba?"::reset();
// calls bar::reset() and baz::reset()
"ba*"::reset();
// calls bar::reset(), baz::reset() and ball::reset()
"*"::reset();
// calls every inherited reset() function.
"ball"::rejoice("Listen to italectro today!");
// only explicit filename of model class allows
// passing arguments to the inherited method
}
AUTHOR
symlynX of PSYC and Nemesis, with a little help from Someone
SEE ALSO
functions(LPC), initialisation(LPC), modifiers(LPC), pragma(LPC),
function_exists(efun), functionlist(efun), inherit_list(efun),
symbol_variable(efun), variable_exists(efun), variable_list(efun).

224
doc/concepts/intermud Normal file
View file

@ -0,0 +1,224 @@
CONCEPT
intermud
DESCRIPTION
There are several intermud protocols which define how (players on)
different muds can communicate with each other. The protocols are
in general not muddriver or mudlib dependant, though the number of
implementations is limited.
This text is about the rather old widely spread 'Zebedee Intermud',
which is also called 'Intermud 2' altough it differs quite a lot
from the real Intermud 2 protocol.
Full information on the newer Intermud 3 could be found on the
web at http://www.imaginary.com/protocols/intermud3.html so there
is no discussion here - the following is just about Zebedee Intermud
(aka Intermud 2).
Zebedee Intermud communication is handled by the /secure/inetd
object, originally written by Nostradamus for Zebedee with some
extensions that are discussed in inetd(C). How the data is
actually sent across the network is described in intermud.basic(C).
SERVICES
Note that the fields "NAME" and "UDP_PORT" should be present in
every message. Very common are the fields "ID" (used whenever an
reply is expected) and "SND" (the sender: he should receive the
reply). These fields will not be mentioned in the list below.
Request types are listed on the leftmost row (e.g. "REQ=channel"),
associated header are listed indented.
"channel"
The channel-request is used for sending a message on any
channel. The "CMD" field is optional and may be omitted for
normal messages. Note that you should not send an history or
list request to _all_ known muds!
"CHANNEL"
The channel on which a message is send (the standard
channels are "intermud", "intercode", "interadm", "d-chat",
"d-code" and "d-adm"; on the d-channels German is spoken)
"DATA"
The message to be send (not used with history/list request)
"CMD"
The body of this header may be:
"" for normal intermud messages,
"emote" if the message is an emote/gemote,
"history" for an history request: the last 20 lines of
this channel will be shown.
"list" to list all remote users listening to this channel
"EMOTE" (optional)
The body is 1 if the message is an emote.
The body is 2 if the message is a gemote.
"finger"
Retreive information about a player or creator on a remote mud.
"DATA"
The player of whom information is requested
"locate"
Check whether a certain player is logged on at a remote mud.
This request is usually send to all known muds at the same time.
"user"
Name of the person who requests the information.
This is used by the sending mud only and has to be included
in the reply.
"vbs"
The verbose option has only two pre-defined values:
1 Even report when the result was negative
2 Don't do timeouts, but keep waiting
This is used by the sending mud only and has to be included
in the reply.
"fnd"
The found option is only used in the reply and it's value
is either 1 (success) or 0 (failure). The absence of a
found parameter indicates failure as well.
"DATA"
The player to find.
"man"
Retreive a manual page from a remote mud. Many muds don't
support this feature...
"DATA"
The name of the requested manual page
"mail"
An extension to the standard protocol, by Alvin@Sushi. This is
used to send mails from one mud to another.
"udpm_status"
This field should only be used in the reply and indicates
how mail is handled. Currently there are four pre-defined
values for the status field:
0 time out
1 delivered ok
2 unknown player
3 in spool (will be delivered later)
"udpm_writer"
Name of the person who wrote this mail
"udpm_spool_name"
Should be returned as sent, this value is used to remove
the mail from the spool directory after it has been
delivered (or refused)
"udpm_subject"
Subject of the mail message
"DATA"
The body of the mail (the actual message)
"ping"
A ping request has only the standard fields, the reply is
usually a short string like " is alive."
"query"
Get standard information about another mud. This is the only
command of which the reply may not include a load of rubbish,
but should only hold the requested information, so that it can
be parsed by the server.
"DATA"
The following queries are pretty much standard:
"commands" List all commands that are supported by the inetd
"email" The email-address of the mud administrator(s)
"hosts" A listing of all hosts in a special format [t.b.d.]
"inetd" The version number of the inetd used
"list" The list of all items which can be queried
"info" A short human-readable string with practically
"query" information
"mud_port" The portnumber that players connect to on login
"time" The local time for this mud
"users" A list of the people that are active in this mud
"version" The version of the mud-driver (and library)
"www" The URL of the mud's web page (e.g.
http://mud.stack.nl/)
"reply"
This request method is used for _all_ replies.
"DATA"
A human-readable string, containing the reply to a given query
"RCPNT"
The same name as in the "SND" field or the query; Usually
this is the name of the player who initiated the query
"QUERY"
This field is only used in a response to a "query" request
and should be equal to the "DATA" field of that request
"vbs"
This field is only used in a response to a "locate" request
and should be equal to the "vbs" field of that request
"user"
This field is only used in a response to a "locate" request
and should be equal to the "user" field of that request
"fnd"
This field is only used in a response to a "locate" request
and should be 1 if the player was located and 0 otherwise
"tell"
Say something to a player on another mud.
"RCPNT"
Name of the player to whom you are talking
"DATA"
Whatever you wish to say to this person
Optional emote-tos are handles are also handled as tells, so
muds without emote-to support display them as reasonable readable
tell message.
"RCPNT"
Name of the player to whom you are talking
"METHOD"
The body of this header may be:
"emote" if the message is an emote
"gemote" if the message is a genitiv emote
"DATA"
The text to be emoted prepended with "*" and appended
with "* ". If you display the emote you have to cut the
stars off. Muds that do not process emote-tos display the
emote as tell message with the stars as indication of
the message's emote meaning.
"who"
List the people that are active on a remote mud. The anwer
usually contains some active information about the players,
like titles, levels or age.
"DATA"
Not supported by many muds. Introduced August 1997.
Additional switch(es) (blanc separated) that change the
appearence of the resulting list. The switches normally
resemble the switches used inside of that mud for the 'who'
command. Typical values include:
"short" "s" "-short" "-s" "kurz":
Return a concise listing.
"alpha" "a" "alphabetisch" "-alpha" "-a"
Sort the players alphabetically.
AUTHOR
Information taken from Outerspaces documentation to be found
on http://mud.stack.nl/intermud/
SEE ALSO
inetd(C), intermud.basic(C), imp(C)

151
doc/concepts/intermud.basic Normal file
View file

@ -0,0 +1,151 @@
CONCEPT
intermud.basic
DESCRIPTION
Here is how intermud data is sent across the internet - specific
for Zebedee Intermud (aka Intermud 2).
ADVANCED PROTOCOL
This file was originally written as a brief outline of the intermud
protocol for use by developers interested in incorperating similar,
compatible intermud protocols into their own mud systems. It is
included here as it provides a much more detailed description of the
intermud protocol than that provided by the original PROTOCOL file,
and hence may be of use to LpMud developers.
PACKET PROTOCOL / FORMAT
All information is transferred as a string via a UDP port (each mud
has 1 send and 1 receive port). This kindof transfer is inherently
unreliable, but it's fast and doesn't use up file descriptors.
The format of the strings (packets) is as follows:
header1:body1|headerN:bodyN|DATA:body-data
In other words, a header name, followed by a : and then the data
associated with this header. Each header/body pair is separated by
the | character. This means that headers and their body cannot
contain the | character. You should check for this in outgoing
packets to aviod decoding errors at the recieving end. The exception
to this is the DATA field. If it is present, it is ALWAYS positioned
at the end of the packet. Once a DATA header is found, everything
following it is interpreted as the body of the DATA field. This
means it can contain special characters without error and it is
used to carry the main body or data of all packets.
By convention, predefined system fields will use capital letters for
field headers and custom headers used by specific applications will
use lowercase names to avoid clashes. The defined system fields are
generally refered to by a set of macros which are defined in a
common header file for clarity.
There is one exception to this header format; If the data is too
large to be transmitted in one single packet, it will be split into
packets of convenient size, each with a special unique packet header
to enable them to be reassembled at the receiving end. These
headers are of the format:
PKT:mudname:packet-id:packet-number/total-packets|rest-of-packet
In this case, the mudname and packet-id combine to form a unique id
for the packet. The packet-number and total-packets information is
used to determine when all buffered packets have been received. The
rest-of-packet part is not parsed, but is stored while the receiver
awaits the other parts of the packet. When/if all parts have been
received they are concatenated and decoded as a normal packet.
PACKET ENCODING / DECODING
Only 2 generic data types are fully suported within the inetd code
itself (namely strings and integers), though others can easily be
used by converting them to one of the supported data types before
transfer and converting back again in receipt. The LpMud "object"
data type is converted to a string automatically by the inetd on
encoding, but no such conversion is carried out on decoding.
On encoding integers are simply converted to a corresponding string.
Strings are left untouched as long as there is no ambiguity as to
wether they should be decoded as a string or an integer. In this
case of ambiguity, the string is prepended with a $ character. If
the first character of a string is the $ character, it is escaped
by prepending another $ character. On decoding, any string with a $
as its first character will have it removed and will then be treated
as a string. Any remaining strings that can be converted to an
integer and then back to a string with no loss of information are
considered to be integers. Any remaining strings are treated as
such and are left unaltered.
DEFINED SYSTEM HEADERS
"RCPNT" (RECIPIENT)
The body of this field should contiain the recipient the message
is to be sent to if applicable.
"REQ" (REQUEST)
The name of the intermud request that is being made of the
receiving mud. Standard requests that should be supported by
all systems are "ping" (PING), "query" (QUERY), and "reply"
(REPLY). The PING request is used to determine wether or not a
mud is active. The QUERY request is used to query a remote mud
for information about itself (look at the udp/query module for
details of what information can be requested). The REPLY request
is special in that it is the request name used for all replies
made to by mud B to an initial request made by a mud A. It is
mud A's responsibility to keep track of the original request
type so that the reply can be handled appropriately.
"SND" (SENDER)
The name of the person or object which sent the request or to
whom replies should be directed. This is essential if a reply
is expected.
"DATA" (DATA)
This field should contain the main body of any packet. It is
the only field that can contain special delimiting characters
without error.
The following headers are used internally by the inetd and should
not be used by external objects:
"HST" (HOST)
The IP address of the host from which a request was received.
This is set by the receiving mud and is not contained in
outgoing packets.
"ID" (ID)
The packet id. This field is simply an integer which is set by
the sending inetd. The number is incremented each time a packet
is sent (zero is never used). This field is only needed if a
reply is expected. REPLY packets _must_ include the original
request id. This is _not_ done by the inetd.
"NAME" (NAME)
The name of the local mud. Used for security checking and to
update host list information.
"PKT" (PACKET)
A special header reserved for packets which have been split.
See PACKET PROTOCOL / FORMAT.
"UDP" (UDP_PORT)
The UDP port the local mud is receiving on. Used for security
checking and updating host list information.
"SYS" (SYSTEM)
Contains special system flags. The only system flag used at
present is TIME_OUT. This is included in packets returned due
to an expected reply timing out to differentiate it from an
actual reply.
UDP REQUESTS / MODULES
The following are standard request types that must be supported
by all systems:
"ping" (PING)
This module should return a REPLY packet that contains the
original requests ID in it's ID field and the SENDER in it's
RECIPIENT field. It should also include an appropriate string
in the DATA field, eg. "Mud-Name is alive.\n"
"query" (QUERY)
This module expects the type of query requested to appear in the
recieved DATA field. It should return a REPLY packet containing
the original ID in the ID field, the SENDER in it's RECIPIENT
field, and the query type in a QUERY field. The DATA field should
contain the information requested.
For details of how other intermud requests operate, look at the
relevant module code.
AUTHOR
Information taken from Outerspaces documentation to be found
on http://mud.stack.nl/intermud/
SEE ALSO
inetd(C), intermud(C)

100
doc/concepts/mccp Normal file
View file

@ -0,0 +1,100 @@
CONCEPT
mccp - The Mud Client Compression Protocol
DESCRIPTION
Informations and code taken from the MCCP Homepage
http://www.randomly.org/projects/MCCP/
MCCP is implemented as a Telnet option [RFC854, RFC855]. The server
and client negotiate the use of MCCP as they would any other telnet
option. Once agreement has been reached on the use of the option,
option subnegotiation is used to determine acceptable compression
methods to use, and to indicate the start of a compressed data stream.
If the driver is compiled with MCCP Support there is a
define __MCCP__.
The driver currently supports both versions of mccp. If your mud
has a H_NOECHO hook you have to find out if the client supports
mccp. Without this hook you still have to start neogotiation.
All sub-negotiation is done by the efuns start_mccp_compress() and
end_mccp_compress() whether you have this hook or not.
Notice: when the client uses compressions all binary_message calls
are executed with flag=3. This is because writing to the
socket would disturb zlib stream.
mccp-efuns:
start_mccp_compress(int telopt) (only needed with H_NOECHO)
end_mccp_compress(int telopt) (only needed with H_NOECHO)
query_mccp(object player)
query_mccp_stats(object player)
Initiating MCCP without H_NOECHO hook:
if(!query_mccp()){
binary_message(({ IAC, WILL, TELOPT_COMPRESS2 }),1)
binary_message(({ IAC, WILL, TELOPT_COMPRESS }),1)
}
the driver will parse the clients answers and start compression.
(The connection might already be compressed, because although the
documentation says clients should not negotiate from themselfes,
zmud e.g. does.)
You can start and stop compression manually by efuns
when you are sure client supports compression :)
Initiating MCCP compression with H_NOECHO hook:
If your mudlib uses the H_NOECHO driver-hook you decided to do
all the negotiation by yourself:
Server Commands
IAC WILL COMPRESS indicates the sender supports version 1 of the
protocol, and is willing to compress data it sends.
IAC WILL COMPRESS2 indicates the sender supports version 2, and is
willing to compress data it sends.
IAC WONT COMPRESS indicates the sender refuses to compress data using
version 1.
IAC WONT COMPRESS2 indicates the sender refuses to compress data
using version 2.
Client Commands
IAC DO COMPRESS indicates the sender supports version 1 of the
protocol, and is willing to decompress data received.
IAC DO COMPRESS2 indicates the sender supports version 2 or above,
and is willing to decompress data received.
IAC DONT COMPRESS indicates the sender refuses to support version 1.
If compression was previously negotiated and is
currently being used, the server should terminate
compression.
IAC DONT COMPRESS2 indicates the sender refuses to support version 2.
If compression was previously negotiated and is
currently being used, the server should terminate
compression
After you found out whether the client supports mccp or not you can
start compression with start_mccp_compress(TELOPT_COMPRESS2) or
start_mccp_compress(TELOPT_COMPRESS). ( you could start it without
checking but some players would protest :) )
AUTHOR
Bastian Hoyer (dafire@ff.mud.de) (some text taken from project page)
HISTORY
Added in LDMud 3.3.447, backported to LDMud 3.2.10.
SEE ALSO
start_mccp_compress(E), end_mccp_compress(E), query_mccp(E),
query_mccp_stats(object player)

56
doc/concepts/memory Normal file
View file

@ -0,0 +1,56 @@
CONCEPT
memory
swapping
DESCRIPTION
TODO: This is out of date. Also document the relation with reset
(Collected from the Changelogs of the driver source)
The swapping algorithm has been changed. A test is done for
every object, comparing to a time stamp. If the object hasn't
been touched for a while, it could be subject for swapping.
Here comes the new thing: the function 'clean_up()' will be
called in the object. If the object still remains, the old
swapping algorithm will continue. That means that objects that
would never be subject to swapping (cloned objects) now have a
chance to self-destruct. It also means that rooms that
contains no important data can self-destruct. Self-destruction
saves more memory than swapping, as swapping only frees the
program code, while self-destruction also frees the internal
object representation.
The call of clean_up() has been modified. There is a constant
in config.h that defines how long time until clean_up is
called in an object. This call is independent of reset() and
swapping. It is recommended that the swapping time is
something short, like 10 minutes to 30 minutes, while the time
to clean_up is longer.
Fixed several bugs in the swap/reset/clean_up logic.
Recommended values are that the swap time is short (less than
30 minutes), and that reset time is medium (aprox 60 minutes),
and that time to clean_up is long (greater than 1.5h hours).
Any feedback of how to best tune these values are welcome. The
call of reset will be done once, and not yet again until the
object has been touched. This enables reset'ed objects to stay
swapped out. If you have a mudlib that has no ojbects that
defines 'clean_up', then you may better define this time as 0,
which means never call clean_up (and thus never swap the
object in needlessly). A well implemented usage of clean_up is
better than the swap algorithm, as even cloned objects can be
cleaned up and a self destruction is more efficient than
swapping (memory wise).
Changed mechanism of calling clean_up() slightly. Only objects
that defines the function will be called. And, only clean_up()
that returns non-zero will be called again. This will minimize
calls of clean_up(), while still cost very litte to maintain.
clean_up() now gets a flag as argument, which will be non-zero
if the the program of this object is used for inheritance by
other objects.
SEE ALSO
clean_up(A), slow_shut_down(M), quota_demon(M), malloc(D)

205
doc/concepts/mysql Normal file
View file

@ -0,0 +1,205 @@
CONCEPT
mysql - mySQL support
DESCRIPTION
On hosts with the mySQL package installed, the driver can be
configured to interface with the mySQL database. If that is done,
the driver defines the macro __MYSQL__ for LPC programs and
activates a number of efuns.
-- Configuration --
Create a dedicated user in the mySQL database for the driver.
Enter this username and password in the file pkg-mysql.c, function
mysql_real_connect(), and compile the driver (the username and
password are built into the driver for security reasons).
If you chose to not create either a username and/or a password,
leave the corresponding entry at 0.
Use mysqladmin to create any databases you want to provide - the
names are later used in the efun db_connect() to connect to
the databases.
-- Usage --
The idea behind SQL-support is that you can swap large amounts of
data into a database where it can be accessed very easily.
As mySQL "limits" the number of connections to 100 and as every
connection to the mySQL-server takes time, you should use
database serverobjects in your MUD which constantly keep the
connection to the mySQL-server.
To connect to your mySQL-server, use the efun db_connect(). It
takes only one argument which is the name of the database (which
must exist). The return-value of db_connect() is an integer
representing the unique handle to the database with which you will
identify your connection later.
To send or retrieve data from this connection, use db_exec(). The
first parameter for all efuns dealing with an open connection is
always the handle and so is the first argument the handle and the
second one the command you want to issue. The return-value is
either 0 if there was an error in your command (this can have
various reasons), otherwise your handle is returned again. A typical
SQL-statement to retrieve data would be like this:
select aliases.command from aliases where (name = 'mario' AND
alias regexp 'l.*')
As you know, mySQL accepts either " or ' to classify strings for
parameters. Most likely, you will pass variables and don't know
whether they contain one or more of these key-chars (or even other
chars that need to be converted). mySQL provides a function for
converting just any string into an acceptable argument and this is
implemented in db_conv_string().
So the above example with variables looks like this:
select aliases.command from aliases where (name ='"+
db_conv_string(name)+"' AND alias regexp '"+
db_conv_string(mask)+"')
I left out the db_exec()-stuff, more complete examples will follow.
After you initiated a statement that should return rows from the
database, use db_fetch() to retrieve the data. db_fetch() returns
the data row by row and not all at once. You need to call it until
it returns 0. THIS IS IMPORTANT! If stop calling db_fetch() before
it reaches the end of data, serious inconsistencies can happen.
If you used a DELETE- or UPDATE-statement, you cannot call db_fetch(),
but you might be interested in the number of deleted/changed rows
which can be queried with db_affected_rows().
After all operations are done in the database, you should use
db_close() to close the connection again. If you are using a
database-server-concept, place it in the remove()-function.
The SQL-efuns have some built-in optimization-features to speed up
often used connections. To get a list of all open connections to the
mySQL-server, use db_handles() which returns an array of integers
with all open handles.
-- Security --
Most SQL efuns (unless execute by the master or the simul-efun object)
trigger a privilege_violation ("mysql", "<efun_name>"). If a more
finegrained control is desired, overload the individual efuns with a
nomask simul-efun.
The unprivileged efuns are:
db_conv_string()
EXAMPLE
A simple server to store aliases could be implemented like this:
/*
** CREATION:
**
** create table aliases (
** name varchar(15) not NULL,
** alias varchar(20) not NULL,
** command varchar(255) not NULL,
** primary key (name, alias));
*/
#define DATABASE "mud"
private int handle;
public void create()
{
handle = db_connect(DATABASE);
}
public int remove()
{
if ( handle )
db_close(handle);
destruct(ME);
return !ME;
}
public int AddAlias(string alias, string command, object ob)
{
if ( !handle )
handle = db_connect(DATABASE);
if ( !db_exec(handle,
"insert into aliases (name, alias, command) values "
"('" + getuid(ob) + "','" + db_conv_string(alias)
+ "','"+
db_conv_string(command) + "')") )
return -1;
return 1;
}
public int RemoveAlias(string alias, object ob)
{
int res;
if ( !handle )
handle = db_connect(DATABASE);
res = db_exec(handle,
"delete from aliases where (name = '"+
getuid(ob) + "' AND alias = '"
+ db_conv_string(alias)+
"')");
if ( !res )
return 0;
res = db_affected_rows(handle);
return (res > 0)?1:-1;
}
public mixed *QueryAliases(string mask, object ob)
{
mixed *result;
string *tmp;
if ( !handle )
handle = db_connect(DATABASE);
if ( !db_exec(handle,
"select aliases.alias, aliases.command from aliases where "
"(name = '" + getuid(ob)+
"' AND alias regexp '" + db_conv_string(mask) + "')") )
return ({ });
result = ({ });
while ( sizeof(tmp = db_fetch(handle)) )
result += ({ tmp });
return result;
}
public string QueryAlias(string alias, object ob)
{
mixed *result;
string *tmp;
if ( !handle )
handle = db_connect(DATABASE);
if ( !db_exec(handle,
"select aliases.command from aliases where "
"(name = '" + getuid(ob)+
"' AND alias = '" + db_conv_string(alias) + "')") )
return 0;
result = ({ });
while ( sizeof(tmp = db_fetch(handle)) )
result += tmp;
return sizeof(result)?result[0]:0;
}
AUTHOR
Mark Daniel Reidel and others.
HISTORY
mySQL support was added as a package in 3.2.8 and became and
integral driver part in 3.2.9.
LDMud 3.2.11 added a privilege_violation() call for each efun.
SEE ALSO
db_affected_rows(E), db_conv_string(E), db_close(E),
db_connect(E), db_exec(E), db_fetch(E), db_handles(E),
db_insert_id(E), db_coldefs(E), db_error(E), privilege_violation(A)

251
doc/concepts/native Normal file
View file

@ -0,0 +1,251 @@
CONCEPT
driver modes / native driver mode
DESCRIPTION
During the evolution of LPMud there has been a hiatus as the
old driver became too restricting for the demands of modern
muds: it did a lot of things the mudlib could do better or
completely different. Removing these things from the driver
weren't a problem, but to keep compatible with the existing
mudlibs (namely the well-known 2.4.5 lib), it was possible to
undo these changes. First by setting a runtime option, then
by compiling the driver either in 'compat' or in 'native'
mode.
Starting with 3.2.1, the distinction between compat and native
mode is more and more transferred into the mudlib, with the
future goal of having a modeless driver. Starting with 3.2.7,
native mode no longer exists as such.
The main mode of the driver is determined at compile time
by preprocessor symbols to be defined/undefined in config.h:
COMPAT_MODE: when defined, the compat mode specifics are activated.
Additional modifications can be achieved by the specification
of commandline arguments (most of them have a default setting
entry in config.h as well):
strict-euids: when active, euid usage is enforced.
Following is the description of the changes (de) activated by
these defines. A shorthand notation is used: 'compat' means
'if COMPAT_MODE is defined' and '!compat' means 'if
COMPAT_MODE is not defined', etc.
Predefined Preprocessor Symbols
If compat, the symbols COMPAT_FLAG and __COMPAT_MODE__ are
defined for all LPC programs.
If strict-euids, the symbol __STRICT_EUIDS__ is defined
for all LPC programs.
For compatibility reasons, the symbol __EUIDS__ is defined
for all LPC programs all the time.
Preloading Of Objects
The driver has the possibility to preload objects before the
game is actually opened to the world. This is done by
calling master->epilog(), which has to return 0 or an array.
If its an array, its elements (as long as they are strings)
are given one by one as argument to master->preload() which
may now preload the objects (or do anything else).
Up to 3.2, compat mode used a different scheme: the file
INIT_FILE (defined in config.h) was read and each line
interpreted as filename of an object to preload. After this
was done, master->epilog() was called. This was dumped in
3.2.1 as it can be simulated by the master.
Initialisation Of Objects
Since 3.2.1, it is task of the mudlib (through the driver
hooks) to call the initialisation lfuns in newly created
objects, so the following table applies only up to 3.2:
mode : init call : reset call
--------------------------------------------
!compat & !native : create() : reset(1)
!compat & native : create() : reset()
compat & !native : reset(0) : reset(1)
compat & native : reset(0) : reset(1)
If INITIALIZATION_BY___INIT was defined, the lfun __INIT()
is called first on creation to initialize the objects
variables.
Movement Of Objects
Since 3.2.1, the efun move_object() is implemented in the
mudlib through driver hooks and the set_environment() efun.
move_object() itself exists just for convenience and
compatibility.
Before 3.2.1, move_object() could applied only to this_object() as
the object to move if in native mode, and called the lfun
exit() in the old environment if in compat mode. As a side
effect, the lfun exit() may not be target of add_action()s
in compat mode.
In !native mode, objects may be moved using the transfer()
efun. It does make assumptions about the design of the
mudlib, though, as it calls the lfuns query_weight(),
can_put_and_get(), get(), prevent_insert() and add_weight().
Efuns In General
creator(), transfer()
These exist only in compat mode (creator() is
identical with getuid() from !compat mode).
getuid()
Exists only in !compat mode (getuid() is identical with
creator()).
file_name(),function_exists()
In !compat mode, the returned filenames start with a
leading '/', in compat mode they don't.
parse_command()
This command exists in two versions: the old is used with
compat, the new with !compat. However,
SUPPLY_PARSE_COMMAND must be defined in config.h in both
cases (this efun is not very useful at all).
process_string()
If this_object() doesn't exist, it defaults to this_player()
and receives the backbone uid (returned by master->get_bb_uid())
as euid. If strict-euids, this uid must not be 0.
snoop()
Besides that the call is verified with a call to
master->verify_snoop(), up to 3.2 the snooper has to have
a non-zero euid.
Userids and Effective Userids
This is probably the most important difference between the
modes.
LPMud always had userids (uids) attributing the objects,
though they were called 'creator names' in compat mode.
Internally, the compat mode uses the 'creator names' as
(e)uid.
With the introduction of native mode, additionally
'effective userids' (euids) were introduced to improve
security handling (which was only a partial success).
The hardcoded handling of euids and uids was quite complex
and too mudlib-insensitive, so most of it got moved from the
driver into the mudlib with 3.2.1.
In strict-euids mode, only objects with a non-zero euid may load
or create new objects.
--- 3.2.1 and after ---
Userids of the Master
The masters (e)uid is determined by a call to
master->get_master_uid().
In strict-euids mode, the result has to be a string,
otherwise the driver won't start up at all. If the result is
valid it is set as the masters uid and euid.
In !strict-euids mode, the result may be any value: 0 or a
string are treated as the uid to set, a non-zero integer
leads to the use of the uid set in the default 'global'
wizlist entry, and any other value defaults to 0.
The euid is either set to the returned string (if any),
or to 0.
The masters uid is determined only on startup this way,
at runtime the uids of a reloaded master determined as
for every object by a call to the appropriate driver
hooks.
Userids of New Objects
To determine the (e)uids for a new object (loaded or
inherited, or cloned), the appropriate driver hook is
evaluated (H_LOAD_UIDS, H_CLONE_UIDS) and the result set
as (e)uid. The result may be a single value, in which case the
euid is set to 0, or an array ({ uid, euid }).
In strict-euids mode, both uid and euid must be 0 or a string,
any other value causes the load/clone to fail.
In !strict-euids mode, the uid (however returned) may also be
a non-zero integer to use the uid of the global
wizlist entry as uid. The euid is then
set to either 0 or the second entry of the returned
array if it's a string.
--- 3.2 ---
LPMud <= 3.2 in !compat mode has the algorithm deciding _which_
(e)uid to set hardcoded into the driver. For this it
features two builtin uids: the root uid (queried by a call
to master->get_root_uid()) and the backbone uid
(master->get_bb_uid()). Both uids should be a string: if
not, the driver in native mode will terminate, else print
a warning to stderr. The backbone uid is queried just once
at startup.
Userids of the Master
In compat mode, the master's (e)uid is the (e)uid as
determined for every other object as well. The following
therefore applies only to !compat mode.
The initial (e)uid is those determined using the normal
algorithm for newly created objects (see below).
After the master being loaded, master->get_root_uid() is
called to query the (e)uid to use for real. If the
result is a string, it is used as real (e)uid, else the
default setting. In native mode, get_root_uid() must
return a string, else the driver will terminate.
Userids of New Objects
If an object has been newly created, it is equipped with
a default uid: 0 in !native mode, "NONAME" in native
mode. The default euid (if needed) is equal to the uid,
or 0 if no master object exists.
The basic idea is simple: the master is asked, which (e)uid
the new object should have.
Objects loaded by the game, in compat model, or by an
object of the same creator will get the suggested e/uid.
Backbone objects will get the euid of the loader.
Other objects will get 0 as euid.
The details are a bit more complicated...:
Then master->creator_file() is asked for the 'creator
uid' (cuid) of this object. If the lfun doesn't exist,
the new object is destructed.
The cuid must be a string, the number 0 or an array of
two elements, !native mode also allows other non-array
values. If the cuid is non of these (native mode
restricts this further to the string or array), the
object is destructed again.
If the cuid is an array of two elements ({ uid, euid }),
these are set as e/uid (non-strings are treated as 0).
If the cuid is neither string, array or the number 0,
the object gets 0 as e/uid (!native mode only).
If the cuid is a string (all other cases are treated
above) is it always set as the objects uid. The objects
euid needs further treatment. If all following cases
(checked in the order listed here) fail, it is set to 0.
In compat mode, the euid is set to the uid.
In !native mode, if the object is loaded (not cloned) or
the loader has as 0 uid, the euid is set to the cuid.
Also in !native mode, if the loader has a 0 euid, the
objects euid is set to 0.
If the cuid is equal to the loaders uid, the objects
euid is set to the cuid == uid.
If the cuid is the backbone uid (and in !native mode the
loader has a non-zero euid), the objects e/uid are both
set to the loaders euid.
------
SEE ALSO
hooks(C), uids(C), move_object(E), initialisation(LPC)

316
doc/concepts/negotiation Normal file
View file

@ -0,0 +1,316 @@
CONCEPT
Telnet Negotiations
DESCRIPTION
The telnet protocol is used to control textbased connections
between a client (the 'telnet' program or a mud client) and a
server (the game driver). Most of the options offered by the
protocol are optional and need to be negotiated between the
client and the server. Consequently, and due to their
specialized nature, mud clients don't have to support the full
telnet option feature set.
For the server to find out if a client supports the telnet
protocol at all, one good approach is to a simple, commonly
used telnet command to the client. If the client reacts
conform to the protocol (or sends telnet commands itself), the
mud can continue to negotiate further options. If the client
does not react, the mud can safely refrain from further
negotiations.
The following list is a more or less comprehensive overview of
the telnet related RFCs (available for example on
http://www.faqs.org/rfcs):
RFC Titel rel. Code
495 TELNET Protocol Specification
513 Comments on the new TELNET specifications
559 Comments on the new TELNET Protocol and its Implem
595 Some Thoughts in Defense of the TELNET Go-Ahead
596 Second Thoughts on Telnet Go-Ahead
652 Telnet Output Carriage-Return Disposition Option NAOCRD 10
653 Telnet Output Horizontal Tabstops Option NAOHTS 11
654 Telnet Output Horizontal Tab Disposition Option NAOHTD 12
655 Telnet Output Formfeed Disposition Option NAOFFD 13
656 Telnet Output Vertical Tabstops Option NAOVTS 14
657 Telnet Output Vertical Tab Disposition Option NAOVTD 15
658 Telnet Output Linefeed Disposition NAOLFD 16
698 Telnet Extended Ascii Option X-ASCII 17
727 Telnet Logout Option LOGOUT 18
728 A Minor Pitfall in the Telnet Protocol
735 Revised TELNET Byte Macro Option BM 19
749 Telnet SUPDUP-OUTPUT Option SUPDUP 22
764 Telnet Protocol Specification
779 Telnet SEND-LOCATION Option SENDLOC 23
818 The Remote User Telnet Service
854 Telnet Protocol Specification
855 Telnet Option Specifications
856 Telnet Binary Transmission BINARY 0
857 Telnet Echo Option ECHO 1
858 Telnet Suppress Go Ahead Option SGA 3
859 Telnet Status Option STATUS 5
860 Telnet Timing Mark Option TM 6
861 Telnet Extended Options - List Option EXOPL 255
884 Telnet Terminal Type Option TTYPE 24
885 Telnet End of Record Option EOR 25
930 Telnet Terminal Type Option TTYPE 24
933 Output Marking Telnet Option OUTMRK 27
946 Telnet Terminal Location Number Option TTYLOC 28
1043 Telnet Data Entry Terminal Option DODIIS Implement DET 20
1053 Telnet X.3 PAD Option X.3-PAD 30
1073 Telnet Window Size Option NAWS 31
1079 Telnet Terminal Speed Option TSPEED 32
1080 Telnet Remote Flow Control Option FLOWCTRL 33
1091 Telnet Terminal-Type Option TTYPE 24
1096 Telnet X Display Location Option XDISPLOC 35
1116 Telnet Linemode Option LINEMODE 34
1143 The Q Method of Implementing TELNET Option Negotia
1184 Telnet Linemode Option LINEMODE 34
1372 Telnet Remote Flow Control Option FLOWCTRL 33
1408 Telnet Environment Option ENVIRON 36
1571 Telnet Environment Option Interoperability Issues
1572 Telnet Environment Option NEWENV 39
2066 Telnet Charset Option CHARSET 42
2217 Telnet Com Port Control Option COMPORT 44
2877 5250 Telnet Enhancements
All negotiations start with the special character IAC which is
defined in /usr/include/arpa/telnet.h (or in
src/driver/telnet.h for 3.2(.1)) and has the decimal value of
255. Negotiations are based on different telnetoptions (their
values are defined in telnet.h too). Before a negotiation can
start the client and the server have to agree that they
support the option.
This works in the following way:
If a client wants to send something to the server it has to
send 'IAC WILL option' (For terminaltype negotation this would
be the 3 bytes 255,251,24; again, check telnet.h) to confirm
that it is able to do that. If the server is supporting that
option and wants to receive something it sends 'IAC DO option'
(255,253,option)
If one side is receiving an 'IAC WILL option' and has not yet
sent with DO or DONT it has to respond with either 'IAC DO
option' if it will support this negotiation or 'IAC DONT
option' if it won't.
If one side is receiving an 'IAC DO option' and has not yet
sent a WILL or WONT it has to reply with either 'IAC WILL
option' if it supports the option or 'IAC WONT option' if not.
A small example: Lets assume we want to negotiating
terminaltype. (TELOPT_TTYPE with value 24). client is the
telnet executable on the playerside, the server is the
gamedriver.
client server
IAC WILL TTYPE
IAC DO TTYPE
Or:
IAC DO TTYPE
IAC WILL TTYPE
After this we are ready to transfer the terminaltype from the
client to the server as explained below.
Now we are ready to start the real negotiations. I explain the
3 options I have currently implemented.
First TerminalType aka TTYPE aka 24 aka TELOPT_TTYPE assuming
the client and the server have exchanged WILL/DO.
The server is now free to send 'IAC SB TELOPT_TTYPE
TELQUAL_SEND IAC SE' which will be replied with 'IAC SB
TELOPT_TTYPE TELQUAL_IS terminaltype IAC SE' where
terminaltype is a non-zero terminated string (it's terminated
by the IAC) (For values look up telnet.h) AND switch the
client's terminalemulation to 'terminaltype'. terminaltype is
case-insensitive. terminal-type may be UNKNOWN. The server may
repeat the SEND request and the client will respond with the
next preferred terminaltype. If this is the same as the
previous received, it marks the end of the list of
terminaltypes. The next SEND request will start the
terminaltypes from the beginning.
Example: (we have exchanged WILL/DO already)
client server
IAC SB TTYPE SEND IAC SE
IAC SB TTYPE IS VT200 IAC SE
IAC SB TTYPE SEND IAC SE
IAC SB TTYPE IS VT100 IAC SE
IAC SB TTYPE SEND IAC SE
IAC SB TTYPE IS VT52 IAC SE
IAC SB TTYPE SEND IAC SE
IAC SB TTYPE IS VT52 IAC SE
/* this marks that we have all terminaltypes. We decide to use the
* vt200 mode so we have to skip to VT200
*/
IAC SB TTYPE SEND IAC SE
IAC SB TTYPE IS VT200 IAC SE
Next important option is NAWS (31) or WindowSizeNegotiation.
This one is a bit easier than terminaltype. After having
received a IAC DO NAWS from the server, the client will reply
with IAC WILL NAWS and immediately after that send IAC SB NAWS
columns_high columns_low lines_high lines_low IAC SE where
xx_low refers to the lowbyte of xx and xx_high refers to the
highbyte of xx. This will be automagically resent at every
windowresize (when the client gets a SIGWINCH for example) or
at your request with 'IAC SB NAWS SEND IAC SE'.
Example: (WILL/DO exchanged)
client server
IAC SB NAWS 0 80 0 24 IAC SE /* the standard vt100 windowsize */
/* no reply */
And, a bit less important but most complex, the LINEMODE (34)
option. It was implemented it due to the fact, that
some weird DOS telnets would not work otherwise. Implemented
are only the absolute basic feature, which is the actual
switching the telnet to linemode. After exchanging WILL/DO the
server sends a modechange request to the client using IAC SB
LINEMODE LM_MODE MODE_EDIT IAC SE, which should turn on local
commandline-editing for the client. If a client supports
LINEMODE it HAS to support this modechange. The client will
reply with IAC SB LINEMODE LM_MODE MODE_EDIT|MODE_ACK IAC SE
(x|y is bitwise or). Thats it for linemode. (You will perhaps
receive other IAC SB LINEMODEs with other LM_xxx ... you may
ignore them. (At least IRIX 5.x sends IAC SB LINEMODE LM_SLC
.... IAC SE which declares the local characterset.)).
Example: (WILL/DO negotiated)
client server
IAC SB LINEMODE LM_MODE
MODE_EDIT IAC SE
IAC SB LINEMODE LM_MODE
MODE_EDIT|MODE_ACK IAC SE
Note: The option is much more funnier as it looks here, it for
example supports a mixed mode between linemode and
charactermode... flushing the input at certain characters (at
ESC or TAB for shell-like commandline completition). We suggest
reading RFC 1184.
You might be interested in TELOPT_XDISPLAYLOC and TELOPT_ENVIRON too.
Now, how to implement this using LDMud?
0. Patch src/driver/comm1.c, function init_telopts() to include
telopts_do[TELOPT_XXX] = reply_h_telnet_neg;
telopts_dont[TELOPT_XXX] = reply_h_telnet_neg;
telopts_will[TELOPT_XXX] = reply_h_telnet_neg;
telopts_wont[TELOPT_XXX] = reply_h_telnet_neg;
for every telnet negotiation you want to use.
Do not overwrite the TELOPT_ECHO and TELOPT_SGA hooks.
Alternatively, set the driver hook H_NOECHO in master.c:
this diverts _all_ telnet data into the mudlib.
1. Add a new driver hook to master.c just below the others.
set_driver_hook(H_TELNET_NEG,"telnet_neg"),
2. Make a telnet.h for your mudlib... just change the arrays in
src/driver/telnet.h.
3. define a function
void telnet_neg(int cmd, int option, int * optargs)
in your interactive objects (login.c , shells, player.c or
whereever). And note, in ALL objects, through which a
player is handed through (in TAPPMud these are login.c and
player.c). [Ok, master.c is interactive for a very short
time too, but it won't accept input, will it?]
'cmd' will be TELCMD_xxxx (see telnet.h), 'option' one of
TELOPT_xxxx and 'optargs' will be an array of ints (bytes in
fact) when 'cmd' is SB.
Parse 'cmd'/'option' and reply with appropiate answers
using binary_message() (appropiate meaning sending the
right DO/DONT/WILL/WONT if not sent before and using the SB
return values).
3.1. Sent IAC DO TTYPE IAC DO NAWS IAC DO LINEMODE at the
first time you can do it (before cat()ing /WELCOME perhaps).
3.2. Note all sent and received WILL/WONT/DO/DONT options for
conforming to the standard, avoiding endless loops and for
easy debugging :)
3.3. Pass those recevied/sent data and other data when the
interactive object is changed (from login.c to player.c or
at other bodychanges). Clear the data when the player goes
linkdead or quits. You won't need to save this data.
3.4. Lower_case() terminaltypes... ;)
3.5. Use reasonable defaultvalues if the client does not
support one of the options. (columns 80,lines 24 if not
NAWS, unknown or vt100 for no terminaltype)
The WILL/WONT/DO/DONT data is best saved in a mapping looking
like this:
([ "received": ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
, "sent" : ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
])
(Ok, it can be done better. But not without confusing *me*
more.)
Before sending anything check
TN["sent"][option,0_if_do_dont_or_1_if_will_wont]
so you don't enter endless loops, save network traffic and the
like.
The windowsize is best saved in the players environment
variables so that he can modify them later on. (Or in two
integers in the player object...). Use for these values is
clear I think.
The terminaltypes received using above mentioned method are
best stored in an array. The actual set terminaltype is best
stored in an environment variable where the player can modify
it. Upon modifying it the IAC SB TTYPE SEND IAC SE cycle
should be started to match the emulation to the entered new
terminaltype. You then may use data retrieved from
/etc/termcap (man 5 termcap) or /usr/lib/terminfo/*/* (SysVID,
man 5 terminfo) to implement terminalcontrol codes dependend
on the terminaltype. /etc/termcap may prove to be the easiest
way tough /usr/lib/terminfo/*/* is the newer (and better) SysV
way of doing it.
[Anyone got a description of the internal terminfo format for
me? -Marcus]
LINEMODE replies may be left alone if only using the mode
change to MODE_EDIT
Some statistics about what clients support telnet negotiations:
Tinyfugue and some other mudclients usually do not support
negotiations.
Except for TF, which supports the Telnet End-Of-Record option
as marker for the end of the prompt. So if you send IAC EOR
after every prompt, it will print the prompt always in the
input window. (Do not forget to negotiate that. First IAC WILL
TELOPT_EOR/wait for IAC DO TELOPT_EOR). Newer versions of
TF will support NAWS and there will be a patch for TTYPE
negotiation available soon.
All telnets able to do negotiations I've encountered support
the TTYPE option.
HP9.x,Irix5.x,Linux,EP/IX,CUTELNET/NCSATELNET (Novell) and
perhaps more support NAWS.
At least Irix5.x,Linux,CU/NCSATELNET support LINEMODE.
SUN does not support NAWS and LINEMODE neither in SunOS 4.1.3
nor in Solaris 2.3.
For getting RFCs you can for example use
ftp://ftp.uni-erlangen.de/pub/doc/rfc/
BUGS
Not all aspects of the options are mentioned to keep this doc
at a reasonable size. Refer to the RFCs to get more confused.
CREDITS
Provided by Marcus@TAPPMud (Marcus Meissner,
<msmeissn@cip.informatik.uni-erlangen.de>).

25
doc/concepts/objects Normal file
View file

@ -0,0 +1,25 @@
CONCEPT
objects
LAST UPDATED
never
DESCRIPTION
An object consists of a collection of functions (also called
'methods') and data (variables) on which the functions operate.
The only way to manipulate the data contained in an object is
via one of the functions defined by the object.
Every single thing in a mud is an object. Rooms are objects.
Weapons are objects. Even your character is an object (a special
kind of object called "interactive" but still an object in most
every respect). Each object (except possibly virtual objects) in
the mud is associated with some file written in LPC (in the mud's
directory structure) that describes how the object is to interact
with the gamedriver and the rest of the objects in the mud.
AUTHOR
Someone
SEE ALSO
files(C), inheritance(C), create(A), reset(A)

33
doc/concepts/rtfm Normal file
View file

@ -0,0 +1,33 @@
CONCEPT
rtfm - read the fucking manual
UPDATE
Mateese, 15-Jun-93, 03:15 MET
SYNOPSIS
rtfm
OPTIONS None, you have to read the manual for an answer.
DESCRIPTION
Used when lazy people ask stupid questions. Normaly cried
out in vain.
FILES
/dev/null
ENVIRONMENT
Any.
CREDITS
Bert Nase, who else?
SEE ALSO
man(H)
DIAGNOSTICS
Is an diagnostic. Since you are reading this you are getting
the idea.
BUGS
Ha!

14
doc/concepts/simul_efun Normal file
View file

@ -0,0 +1,14 @@
CONCEPT
simul_efun
DESCRIPTION
The simul_efun object is automagically sort-of inherited by
every object. That functions that are defined in it can be
accessed just like efuns or inherited functions by every
object (except the master object). To get access to efuns that
are overloaded by the simul_efun object, you can use the
efun::function() to bypass the simul_efun (unless the
simul_efun object has defined the function as ``nomask'').
SEE ALSO
get_simul_efun(M), inheritance(LPC), operators(LPC)

73
doc/concepts/tls Normal file
View file

@ -0,0 +1,73 @@
PRELIMINARY
CONCEPT
tls (transport layer security)
DESCRIPTION
TLS stands for Transport Layer Security which is the successor
of the well known SSL (Secure Socket Layer). Both techniques
provide a way to authenticate and encrypt the data send through
a network connection.
By enabling TLS during compilation of the driver you can provide
a secure channel into the mud to your players.
In difference to other solutions as "sslwrap" or "stunnel" the
driver integrated approach has the advantage that the mud sees
the real IP of the player, not the IP of the local mud host.
USAGE
To use TLS configure your driver with --enable-tls option.
After starting your driver you have five new efuns
(tls_init_connection(), tls_deinit_connection(), tls_error(),
tls_query_connection_info(), tls_query_connection_state()).
You can switch on TLS by calling tls_init_connection().
This can happen in three ways:
1) in telnet_neg()
Advantage of this method is that you can offer TLS on a normal
mud port. If you have a limited number of ports this can
become important. The TLS connection will be started by
the client with help of telnet option STARTTLS. Currently
there are no mudclients that support this method.
You will have to implement the telnet option STARTTLS (46) for
this method. The draft for this can be found here:
http://www.ietf.org/proceedings/99mar/I-D/draft-ietf-tn3270e-telnet-tls-01.txt
Call tls_init_connection() to initiate the TLS handshake.
2) in master_ob->connect()
Advantage of this method is that your users can connect with
any program that supports TLS/SSL. Examples are telnet-ssl,
sslwrap or stunnel. Disadvantage is that you have to spend
a dedicated port for this.
You have to call tls_init_connection() as first command
after the player connected (normally in master_ob->connect())
3) in an interactive object using a callback.
This method is similar to method (1), but not limited to
telnet: it is useful for implementing protocols thta use
STARTTLS like SMTP or IMAP. tls_init_connection() can be
called at any time by the interactive object.
You must not write to the connection after calling this
efun until the callback is executed (the prompt will
be supressed automatically during this time).
To test your code, you can use the openssl binary.
`openssl s_client -connect host:port' should display your certificate
and anything you write after the callback is executed. If you
encounter the error message `SSL3_GET_RECORD: wrong version number'
you're probably writing to the connection while you should not.
BUG
This manpage might be not quite up to date with the implementation.
HISTORY
Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
SEE ALSO
tls_* efuns

96
doc/concepts/uids Normal file
View file

@ -0,0 +1,96 @@
CONCEPT
uids (userids)
DESCRIPTION
Every object in the mud is attributed with a user-id 'uid': a string
which associates the object with a certain 'user' (aka 'wizard' or
'creator', though it is not limited to that). The uid can be 0, which
internally is the default-uid.
The uid serves a dual purpose: on the on hand it is used to gather
statistics about the various groups of objects (in the famous
'wizlist'), on the other hand the uid can come in handy in the
implementation of security systems.
The uid of an object is assigned at its creation through the
driver hooks H_LOAD_UIDS for loaded objects, and H_CLONE_UIDS
for cloned objects, and can't be changed afterwards.
The uid of an object can be queried with the efun getuid() (resp.
creator() in compat-mode).
Every object also has a second string attribute, the 'effective
userid' or 'euid', which also may be 0. This value was intended to
implement a security system based on difference between theoretical
and effective permissions. Since the effectiveness of this system is
doubtful, the driver enforces such a use only as an option.
As uids, euids are assigned at an objects creation through
the two aformentioned driverhooks. They can be queried with
the efun geteuid() and changed with the efun seteuid(). Calls
to the latter are verified by the master lfun valid_seteuid().
Additionally objects can impose their uid onto an other objects
euid with the efun export_uid().
If the driver is run in 'strict euids' mode, euids are taken
more seriously than being just another attribute:
- all objects must have a non-0 uid.
- objects with a 0 euid can't load or clone other objects.
- the backbone uid as returned by master::get_bb_uid() must
not be 0.
Userids are assigned at the time of the creation of an object
by calling the driverhooks H_LOAD_UIDS and H_CLONE_UIDS:
mixed <load_uids closure> (string objectname)
mixed <clone_uids closure>(object blueprint, string objectname)
When an object is newly loaded, the H_LOAD_UIDS hook is
called with the object name as argument.
When an object is cloned, the H_CLONE_UIDS hook is called
with the blueprint object as first and the clone's designated
name as second argument.
In both cases the new object already exists, but has 0 uids.
For the result, the following possibilities exist (<num> is
a non-zero number, <no-string> is anything but a string):
"<uid>" -> uid = "<uid>", euid = "<uid>"
({ "<uid>", "<euid>" }) -> uid = "<uid>", euid = "<euid>"
({ "<uid>", <no-string> }) -> uid = "<uid>", euid = 0
If strict-euids is not active, the following results are
possible, too:
<num> -> uid = 'default', euid = 0
({ <num>, "<euid>" }) -> uid = 'default', euid = "<euid>"
({ <num>, <no-string> }) -> uid = 'default', euid = 0
Slightly different rules apply to the (e)uid of the master.
The masters (e)uid is determined by a call to
master->get_master_uid():
"<uid"> -> uid = "<uid>", euid = "<uid>"
In non-strict-euids mode, more results are possible:
0 -> uid = 0, euid = 0
<num> -> uid = 'default', euid = 0
If your uids are in general based on filenames, it is wise to return a
value here which can not be legally generated from any filename. OSB
for example uses 'ze/us'.
The masters uid is determined only on startup this way, at runtime the
uids of a reloaded master determined as for every object by a call to
the appropriate driver hooks.
SEE ALSO
native(C), get_bb_uid(M), valid_seteuid(M),
objects(C), clone_object(E), geteuid(E), getuid(E), seteuid(E)

241
doc/driver/codestyle Normal file
View file

@ -0,0 +1,241 @@
The LPMud gamedriver is by nature the result of the cooperative work
of multiple programmers, often separated by large oceans and years
of time. In order to keep the appearance of the driver source consistent
(and with that maintainable), the following guidelines should be followed
for all code contributions.
For a quick start in how good driver source should look like, take
a look at comm.{c,h}, object.{c,h} and mapping.{c.h}.
The guidelines have a strong emphasis on code layout and commenting,
stemming from the fact that proper layout and comments gave the
incentive for LDMud in the first place. Right now, 50% of all lines
are comments, and that is a Good Thing.
Language
--------
The language is ISO Standard C (also known as 'C89' or 'ANSI C').
Common compiler extensions or features from the new C99 standard are
permitted if their addition is transparent for other C89 compilers.
For example: the 'inline' keyword permitted through the use of the
INLINE macro; so are the Metrowerks-pragmas and GNU-attributes. Not
permitted are GNU's local functions.
System/Platform specifics are to be detected by the configure script
and provided with transparent implementations in port.{c,h} (or for
module-specific dependencies in the respective modules).
Adherence to the Standard has the following implications:
- All functions must be fully prototyped.
- Standard types like size_t, ssize_t or ptrdiff_t are to be used
whereever possible.
- Unixisms like
{
a = malloc(20);
b = malloc(20);
c = b-a;
}
are not legal and shouldn't be used.
- Don't make assumptions about the size of the base types (e.g.
a char might have more than 8 bits). If such an assumption
is unavoidable, comment it clearly and if possible add a test
to raise a compile or runtime error if the assumption is not met.
Style
-----
All modules (.c-files) have to have their interface in an accompaning
.h-file. The header file must be guarded against repeated inclusion
with the normal
#ifndef HEADERNAME_H
#define HEADERNAME_H 1
...
#endif /* HEADERNAME_H */
construct. To use a module, its headerfile must be included - no 'extern'
declarations.
Every module must include "drivers.h" which in turn includes the
portability headers and provides common defines.
Use the driver provided types and macros like BOOL or p_int.
Code should be written defensively and readable. This is not the IOCCC.
No magic numbers - use #defines to give them names.
Add sanity checks where useful. If the checks are costly, enclose
them in a #ifdef DEBUG...#endif bracket.
Comment questionable code or opportunities for possible extensions with a
'TODO: ...' comment. For multiline comments, use 'TODO::' on the second
and following lines (this makes reading a grep easier).
Comment temporary debug code with a 'DEBUG:' comment. Similar, debug
output should always begin with 'DEBUG:'.
Variable identifiers should start with a lowercase letter, function
identifiers may start with upper or lowercase, constant identifiers
should start with an uppercase letter. Macro identifiers should be
all UPPERCASE, other identifiers may be under_scored or SkudlyCaps.
Hungarian notation is accepted only in a very light form: pFoo for
pointers, ppFoo for pointer to pointers, iFoo for integer types,
sFoo for string pointers, aFoo for complex types - you get the
idea. But no alpzsFoo and friends.
f_xxx() function names are reserved for efun implementations.
typedef'd typenames should end in _t (e.g. 'mapping_t'), struct
names should end in _s (e.g. 'struct instrs_s').
Indentation is 4 spaces per level. No tab characters anywhere!
The indentation style is a variant of the 'Allman style':
if (foo)
{
...body...
} /* if (foo) */
Note the comment at the closing brace!
One line bodies may be written as
if (foo) body;
or
if (foo)
body;
_if_ it improves the readability.
Similar, the typical layout of a function is:
static int
function_name ( arg1 , arg2)
{
....
} /* function_name() */
If an expression (argument list, ...) extends over several, the first
literal element on a line should be an operator or syntactical marker:
if (condition1
&& ( condition2
|| condition3)
)
printf( "..."
, arg1, arg2, arg3
);
Be generous with whitespace - both horizontal and vertical.
[ The reasoning behind this style is to use the language elements
to create strong visual structures for the eyes to follow. By doing so,
the structure of the program becomes obvious without much
conscious thought.
]
Commenting
----------
The comments also follow the idea of giving strong visual clues of
how the program is structured.
Horizontal lines should be created as
/*------...-------*/
/*======...=======*/
/* - - -... - - - */
The '---' line is the normal separator between the components of a
source file (includes, variable declarations, macro declarations,
the separate functions). The '===' line can be used to separate
larger sections of a source file (e.g. the lowlevel routines from
the efun implementations). '- -' lines, which usally span less than
the whole line, can be used to subdivide large functions (though then
it's often better to split the function into several small ones).
A '***' line is reserved for the end of every source file.
A '/* --- Subsection --- */' is also good to separate subsections.
Vertical lines are to be constructed as
/*
*
*/
No box comments.
Every function must have a head comment explaining the meaning
of the arguments, what the function does, and the possible results.
For efun implementations, this comment should be identical to the
man page.
Within a function, every variable should be commented as
int foo; /* short comment */
int bar;
/* long comment which doesn't fit on one line.
*/
The major steps in a function should be preceeded by a comment
explaining them. Also, wherever a critical design decision has
been made, a comment should line out the whats and whys:
/* Duplicate the stored arguments for the function call.
* (It's tempting to use the stored arguments directly
* in the last pass; however it's not guaranteed that
* the last pass actually comes this far.)
*/
A typical file layout, commentwise, looks like this:
/*------------------------------------------------------
* Gamedriver Bouncing module.
*
* <reserved for future copyright notice>
*------------------------------------------------------
* 'Big picture' description of the module and its
* relation to the other gamedriver parts.
*
* Tricky design discussions also belong in here.
*------------------------------------------------------
*/
#include "driver.h"
#include "typedefs.h"
#include <stdlib.h>
#include "bounce.h"
#include "backend.h"
/*--------------------------------------------------------*/
/* --- User information --- */
interactive_t *all_bouncers[MAX_PLAYERS];
/* --- Statistics --- */
p_int number_of_calls;
/*--------------------------------------------------------*/
void
add_bouncer (interactive_t *bouncer)
/* This function adds <bouncer> to the all_bouncers[].
*/
{
int i; /* search index */
....
} /* add_bouncer() */
/**********************************************************/

252
doc/driver/copyright-issue Normal file
View file

@ -0,0 +1,252 @@
The question of the Copyright and Terms of Use for the driver is an old
one. Here is what I could salvage from the amylaar-users mailing list.
Important is Jacob's mail I received in 1999 which essentially casts
the current copyright terms in stone.
-- Lars (not /Lars)
-------------------------------------------------------------------------------
Date: Sat, 13 Nov 1993 03:11:29 +0100 (MET)
From: amylaar@meolyon.hanse.de (Joern Rennecke)
Subject: LPmud Copyright
To: busey@ux1.cso.uiuc.edu (busey andrew), lars@cd.chalmers.se,
croes@swi.psy.uva.nl, gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de,
jacob@cd.chalmers.se, r_behren@informatik.uni-kl.de,
mud@alijku05.edvz.uni-linz.ac.at, alcaman@cs.tu-berlin.de
Motivation of this letter:
There seems to be a potential for muds that run on dedicated machines
that charge fees from player to make the mud economically feasible.
The Copyright file says that LPmud can freely used iff it is not for
monetary gain. Now the debate what constitutes monetary gain and if
an individual license is an license to break the copyright,
is an interpretation of the license in Copyright or gives rights
independent the restrictions in Copyright has become a normal flame
war in the rec.games.mud.* groups. That is to say, one of the worst
thinkable.
To allow muds to charge fees to cover costs, without going through
such debates every time, I suggest to amend the Copyright file
with terms under witch such a mud is considered to comply to the
'no monetary gain clause' .
Explanation of the recipient list and some individual messages:
Busey Andrew: wants to set up a mud that charges fees to cover costs.
If the below rules won't make it into the Copyright, you can regard this
as a license - of course only for the code written by me.
Lars Pensj|: original author.
Please forward this letter to other authors that have contributed to 3.1.2
who have a say in the copyright.
Felix A. Croes: wrote the non-corrupting indentation code for ed.
Sean A Reith: wrote Mud-sprintf() .
Lars Duening: wrote the Amiga port.
Reimer Behrends: wrote mergesort based sort_array() .
Herp: wrote get_object_actions() .
Jacob Hallen: is one of the people involved with the CD driver; the email
address was in the news recently...
Please forward this letter to the person holding the copyright for the
UDP connectivity(unless it's yourself :-) .
Alexander Weidt:
Please try to forward this letter to my brother...
I hope to finally get terms which all autors can agree on that can be included
into the Copyright file. I suggest group replies, so that we can get some
kind of discussion going (unless there is immediate approval from all
authors :-) . When you have objections, please try to point out what is
wrong with these terms. Even better would it be if you had a solution
to the problem.
Joern Rennecke (Amylaar)
Proposed Terms:
1. A LPmud may charge fees from players to cover running and machine costs.
2. Running costs in these terms are the cost for the network connection,
electric power to operate the host machine, wear of backup media,
repair costs for the host machine, and cost for a bank account.
For the costs of a bank account to be considered runnung costs,
they must not habe been considered according to 8. , and the
institute has to be choosen with at least usual consideration on
terms and costs of the account and that there must be no affiliaton
with the institute.
3. Money to cover running costs for a maximum of 18 month may be accumulated
in advance from fees to smoothe fluctation and to ensure stability of
the mud. The spare money has to be kept separate from personal money
and should be invested in trustee investment if liquidity allows.
If the mud is permanently put down, this money has to be refounded to the
playeres.
4. Machine costs are costs for buying, installation and upgrade of the host
machine. The costs have to appear on a bona fide purchase / service
contract with a person/institution that is not affiliated with the
person who sets up the mud.
When the host machine is put out of use, or parts of it are removed for
other than technical reasons, are parts are nor re-inserted after the
technical resons for removal and not re-inserting have become void,
the current value of the machine that has put out of use/the removed
parts is to be subtracted from the machine costs.
If thus more money has been paid for machine costs than there are
currently, the surplus amount has to be refounded to the mud players.
5. The machine cost share in the fee may not be more than 1/2400th
of the machine costs per month. If the mud has less than 100 players,
it may be up to machine costs / 24 / number of players, but not more than
1/120th of the machine costs per month.
6. When money has to be payed back to the mud players, only those that
have payed at least once a fee within the last 24 month are to be
considered. For these players, the money is distributed in the ratio
of the all fee shares ever payed to cover machine costs.
7. All players pay equal fees.
8. Banking costs that have to be paid by the mud administration and are
immediately connected to incoming money transactions can be subtracted
from the transferred amount before counting it as payment of fees,
provided that the institute was choosen with at least usual
consideration on terms and costs of the account, and that there is
no affiliaton with the institute.
9. The amount of voluntary donations is unlimited. A donation is not
considered voluntary if it is connected with special features or
favours in the mud other than an hounarary mentioning of the donor,
or if the donor is made to believe that such a connection exists.
Reasonable measures have to be taken that there is no
misunderstanding on this point.
Comments:
3. You may not use the money of the mud to bridge personal inliquidity.
Don't gamble with other persons money, e.g. investing it in junk bonds.
5. Fees should not be arbitrarily raised so that players can be driven
out. I considered a fixed minimal distributen of the costs to be
the best means to codify this.
Absolute figures are bound to become void by inflation.
6. The 24 month period is introduced to allow to erease records of
clients after two years, and to keep overhead affordable.
7. We don't want favourites to get a free lift, and others grudgingly
paying extra. If you think somebody needs free access, find someone
who pays for it, or make a found payed from voluntary donations.
-------------------------------------------------------------------------------
Date: Fri, 19 Nov 1993 17:10:44 +0100 (MET)
From: Lars Pensj| <lars@cd.chalmers.se>
Subject: Re: LPmud Copyright
To: amylaar@meolyon.hanse.de (Joern Rennecke)
Cc: busey@ux1.cso.uiuc.edu, lars@cd.chalmers.se, croes@swi.psy.uva.nl,
gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
r_behren@informatik.uni-kl.de, mud@alijku05.edvz.uni-linz.ac.at,
alcaman@cs.tu-berlin.de
I agree that fix of the copyright is needed. I would prefer to use the
Gnu Copyleft, as I don't care any longer if anyone makes money from it. The
important thing is that it is free, which means noone will be able to make
much money anyway.
Any thoughts about it ?
/Lars
-------------------------------------------------------------------------------
Date: Fri, 19 Nov 1993 20:14:10 +0100 (MET)
From: Jacob Hallen <jacob@cd.chalmers.se>
Subject: Re: LPmud Copyright
To: amylaar@meolyon.hanse.de (Joern Rennecke)
Cc: busey@ux1.cso.uiuc.edu, lars@cd.chalmers.se, croes@swi.psy.uva.nl,
gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
r_behren@informatik.uni-kl.de, mud@alijku05.edvz.uni-linz.ac.at,
alcaman@cs.tu-berlin.de
> Jacob Hallen: is one of the people involved with the CD driver; the email
> address was in the news recently...
> Please forward this letter to the person holding the copyright for the
> UDP connectivity(unless it's yourself :-) .
I represent everyone involved in the CD driver. The UDP stuff is to be
considered public domain. All other parts are covered by the non-profit clause.
Code origination from me, Johan Andersson (Commander), Ronny Wikh (Mrpr),
Lennart Augustsson (Marvin) is covered by it. We have no intention of
allowing people to charge money for the usage of our driver, or borrowed
pieces thereof.
We consider the acceptance of volontary donations as fair practice, and we
can accept the charging for the use of extra equipment needed to allow
people to access the mud, as long as there is a reasonable way to access the
mud without being charged. (Providing modem access at a cost while allowing
free access over the internet is an example of such a setup.)
My personal view is that an elaborate setup of terms like the one in the
original letter is unreasonable. It is designed for a very specific set of
circumstances. It is impossible to check and it is very bureaucratic.
It does not have my support.
Jacob Hallen
-------------------------------------------------------------------------------
Date: Sat, 20 Nov 1993 23:35:12 +0100 (MET)
From: Multi User Dungeon <mud@alijku05.edvz.uni-linz.ac.at>
Subject: Re: LPmud Copyright
To: lars@cd.chalmers.se (Lars Pensj|)
Cc: busey@ux1.cso.uiuc.edu, croes@swi.psy.uva.nl, gusar@uniwa.uwa.OZ.AU,
duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
r_behren@informatik.uni-kl.de, alcaman@cs.tu-berlin.de
Lars> important thing is that it is free, which means noone will be able to make
Lars> much money anyway.
You are speaking about the GD,correct ? Normally, many a site uses an unmodified
GD based upon which is a more or less heavily or not heavily Mudlib. Based upon
this Mudlib is the work of the `wizards' ... Sorry for repeating known stuff.
This makes most Muds differ from each other. So, the fact that the GD itself
is free, doesn't imply that you won't make money.
Another point to argue: maintainig a Mud takes time .. a LOT of time. Usually,
doing so is not fun at all. I experienced that the more players you have, the
less fun it is fore the adminstrators. You spend a lot of time coding,
searching and fixing bugs ... and I think, this can be regarded as a
service for players (... and players really can be a pain sometimes ...)
Would it be legal to charge money for that ?
Another thought: Internet Muds. They run on internet, usually on computers
owned by a school or university, some with, some without the knowledge of
the site adminstrators. Would it be legal to charge money when you run
a Mud on equipment not owned by yourself ? And, even if you own the computer,
do you pay for the internet link ? If not, I fear you must not charge money
for a Mud without speaking with the network adminstrator since you are using
the network components (router/bridges, even cables :-> ...) for free.
How difficult is charging money in European Muds ? Not that I plan
to do so for HM (it's closed for players currently anyway), but isn't there a
big difference according to the "accounting mechanism" (ugh, bad english :-)
that is used in the States ? I heard that it is much more easy to make
financial transactions within the States. So, I suspect the "Mud money charging"
discussion arrives from the US :-)
Greetings, Herp (mud@mud.uni-linz.ac.at)
------------------------------------------------------------------------------
From jacob@cd.chalmers.se Sun Oct 24 17:02:53 1999
Newsgroups: rec.games.mud.admin
Subject: Re: Newsgroups
From: jacob@cd.chalmers.se (Jacob Hallen)
Date: 24 Oct 1999 16:02:53 GMT
In article <380f817d.35916528@news.earthlink.net>,
Lars Duening <lars@bearnip.com> wrote:
>On Thu, 21 Oct 1999 12:11:36 -0700, Ilya
><ilya@spam.free.gamecommandos.com> wrote:
>
>>Hey Lars, what hope is there, if any, of getting
>>something going in the LP world, using LDmud or
>>whatever, that can be used commercially?
>
>Unfortunately only slim hope: the parts written by the Genesis folks
>are definitely non-commercial; but rewriting is difficult because
>nobody remembers _which_ parts are concerned (and mailed requests
>haven't been answered). And I haven't asked the other contributors
>yet, either.
I have a pretty good idea of who wrote what parts of the early version 3
gamedrivers. All in all there have been about 30 people involved. Unless
you recode the entire gamedriver from scratch, or build from assembled pieces
with known copyright restrictions, there is no way you can come up with
something that does not infringe on the rights of someone who will not
allow commecial use.
Jacob Hallén
------------------------------------------------------------------------------

17
doc/driver/debugmalloc Normal file
View file

@ -0,0 +1,17 @@
NAME
debugmalloc
DESCRIPTION
This command is hardcoded into the driver.
Toggles the debug mode for the memory managment.
If the O_IS_WIZARD flag is used in the mudlib (i.e. if
set_is_wizard() was called), this command is allowed only for
users that have this flag set.
HISTORY
Deactivated in 3.2.7 by default, it was effectless before anyway.
SEE ALSO
malloc(D), status(D), memory(C), objects(C), debug_info(E),
set_is_wizard(E)

15
doc/driver/driver Normal file
View file

@ -0,0 +1,15 @@
NAME
driver
DESCRIPTION
This directory contains descriptions of miscellaneous
internals of Amylaar's version of the LPC parser/interpreter,
that might be useful to know.
One thing described here are the special hardcoded commands
of the interpreter for querying the status of the memory
management.
SEE ALSO
efun(E), applied(A), concepts(C), master(M), lpc(LPC),
malloc(D), status(D)

49
doc/driver/dumpallobj Normal file
View file

@ -0,0 +1,49 @@
NAME
dumpallobj
DESCRIPTION
Write a list of all loaded or cloned objects into the file
OBJ_DUMP, and a list of all destructed objects into the
file DEST_OBJ_DUMP. Both files are located in the root directory of
the mudlib.
Warning: these files can be very large, and if the driver is low
on memory there is a certain crash probability.
If the O_IS_WIZARD flag is used in the mudlib (i.e. if
set_is_wizard() was called), this command is allowed only for
users that have this flag set.
For every live object, a line is written into the file with the
following information in the given order:
- object name
- size in memory, shared data counted only once
- size in memory if data wouldn't be shared
- number of references
- 'HB' if the object has a heartbeat, nothing if not.
- the name of the environment, or '--' if the object has no
environment
- in parentheses the number of execution ticks spent in this
object
- the swap status:
nothing if not swapped,
'PROG SWAPPED' if only the program is swapped
'VAR SWAPPED' if only the variabes are swapped
'SWAPPED' if both program and variables are swapped
- the time the object was created
For every destructed object, a line is written into the file with the
following information in the given order:
- object name
- number of references
- 'NEW' if the object was destructed in this executiong
thread, nothing if it is older already.
HISTORY
LDMud 3.2.9 added the DEST_OBJ_DUMP.
LDMud 3.2.10 added the object creation time to OBJ_DUMP.
SEE ALSO
malloc(D), status(D), memory(C), objects(C), debug_info(E),
set_is_wizard(E)

307
doc/driver/invocation Normal file
View file

@ -0,0 +1,307 @@
NAME
driver/invocation
PURPOSE
Description of the invocation of the gamedriver, especially of the command
arguments. This document describes the commandline version of the driver
only; non-commandline versions are platform specific and described in
the related documentation.
DESCRIPTION
The driver is invoked from the commandline as other normal programs.
Neither the current directory nor the directory the executable is in need
to be in any special relation the directory of the mudlib. Once the driver
is running, it emits two streams of outputs:
- driver-related messages on stderr; this unfortunately includes
LPC compiler diagnostics
- LPC runtime-related messages in the logfile <mudlib>/<host>.parse.log
(the name can be changed).
It is possible to start the driver without any commandline arguments as
suitable defaults are specified at compile time. The invocation syntax
is:
driver [options] [<portnumber>]...
<portnumber> the number of the port the driver shall use to accept
connections. The maximum number of ports is determined by MAXNUMPORTS
in the source file config.h.
The options modify the behaviour of the gamedriver. Some of them are only
available if a certain compile-time option was enabled (typically in
the source file config.h). The following options are recognized:
-P|--inherit <fd-number>
Inherit filedescriptor <fd-number> from the parent process
as socket to listen for connections.
Only available if compiled with MAXNUMPORTS.
-u|--udp <portnumber>
Specify the <portnumber> for the UDP port, overriding the compiled-in
default.
Only available if compiled with CATCH_UDP_PORT.
-D|--define <macro>[=<text>]
Add <macro> (optionally to be expanded to <text>) to the list of
predefined macros known by the LPC compiler.
-E|--eval-cost <ticks>
Set the number of <ticks> available for one evaluation thread.
If 0, execution is unlimited.
-M|--master <filename>
Use <filename> for the master object.
-m|--mudlib <pathname>
Use <pathname> as the top directory of the mudlib.
--debug-file <filename>
Log all debug output in <filename> instead of
<mudlib>/<host>.debug.log .
--hostname <name>
Use <name> as hostname instead of what the system says.
--hostaddr <addr>
Use <addr> as address of this machine, instead of what the
system says. In particular this address will be used to open
the driver ports.
--no-compat
--compat
Select the mode (plain or compat) of the driver.
This choice does not affect the default name of the master object.
-d|--debug
Generate debug output; repeat the argument for even more output:
>= 1: log resets, clean ups, swaps, reception of urgend data,
telnet negotiation states.
check_a_lot_of_refcounts() on startup when swapping of
variables is disabled.
>= 2: log all add_message()s, name lookup failures, new players.
>= 3: progress of garbage collection
>= 4: even more junk from garbage collection
-c|--list-compiles
List the name of every compiled file on stderr.
-e|--no-preload
Pass a non-zero argument (the number of occurences of this option)
to master->preload(), which usually inhibits all preloads of castles
and other objects.
--erq <filename>
--erq "<filename> <erq args>"
Use <filename> instead of 'erq' as the basename of the ERQ executable.
If the name starts with a '/', it is take to be an absolute pathname,
otherwise it is interpreted relative to <bindir>. If not specified,
'erq' is used as the executable name.
By enclosing the argument value in quotes, it is possible to pass
arguments (e.g. --execdir) to the erq. These arguments however must
not contain embedded spaces.
-N|--no-erq
Don't start the erq demon (if it would be started at all).
-t|--no-heart
Disable heartbeats and call_outs.
-f|--funcall <word>
The lfun master->flag() is called with <word> as argument before the
gamedriver accepts netword connections.
--max-array <size>
The maximum number of elements an array can hold.
Set to 0, arrays of any size are allowed.
--max-mapping <size>
The maximum number of elements a mapping can hold.
Set to 0, mappings of any size are allowed.
--max-callouts <size>
The maximum number of callouts at one time.
Set to 0, any number is allowed.
--max-bytes <size>
The maximum number of bytes one read_bytes()/write_bytes() call
can handle.
Set to 0, reads and writes of any size are allowed.
--max-file <size>
The maximum number of bytes one read_file()/write_file() call
can handle.
Set to 0, reads and writes of any size are allowed.
--max-thread-pending <size>\n"
The maximum number of bytes to be kept pending by the socket write
thread.
Set to 0, an unlimited amount of data can be kept pending.
This option is ignored if pthreads are not used.
--cleanup-time <time>
The idle time in seconds for an object before the driver tries to
clean it up. It should be substantially longer than the reset time.
A time <= 0 disables the cleanup mechanism.
--reset-time <time>
The time in seconds before an object is reset. A time <= 0 disables
the reset mechanism.
-s <time> | --swap-time <time>
-s v<time> | --swap-variables <time>
Time in seconds before an object (or its variables) are swapped out.
A time less or equal 0 disables swapping.
-s f<name> | --swap-file <name>
Swap into file <name> instead of <mudlib>/LP_SWAP.<host> .
-s c | --swap-compact
Reuse free space in the swap file immediately.
Giving this option results in smaller, but also more fragmented
swapfiles, and the swap performance may degrade.
--max-malloc <size>
Restrict total memory allocations to <size> bytes.
A <size> of 0 or 'unlimited' removes any restriction.\n"
--min-malloc <size>
--min-small-malloc <size>
Determine the sizes for the explicite initial large resp. small chunk
allocation. A size of 0 disables the explicite initial allocations.
-r u<size> | --reserve-user <size>
-r m<size> | --reserve-master <size>
-r s<size> | --reserve-system <size>
Reserve <size> amount of memory for user/master/system allocations to
be held until main memory runs out.
--filename-spaces
--no-filename-spaces
Allow/disallow the use of spaces in filenames.
--strict-euids
--no-strict-euids
Enable/disable the enforced use of euids.
--pidfile <filename>\n"
Write the pid of the driver process into <filename>.\n"
--tls-key <pathname>
Use <pathname> as the x509 keyfile, default is 'key.pem'.
If relative, <pathname> is interpreted relative to <mudlib>.
--tls-cert <pathname>
Use <pathname> as the x509 certfile, default is 'cert.pem'.
If relative, <pathname> is interpreted relative to <mudlib>.
--tls-trustfile <pathname>
Use <pathname> as the filename holding your trusted PEM certificates.
If relative, <pathname> is interpreted relative to <mudlib>.
--tls-trustdirectory <pathname>
Use <pathname> as the directory where your trusted
PEM certificates reside, default is '/etc/ssl/certs'.
If relative, <pathname> is interpreted relative to <mudlib>.
--wizlist-file <filename>
--no-wizlist-file
Read and save the wizlist in the named file (always interpreted
relative the mudlib); resp. don't read or save the wizlist.
--gcollect-outfd <filename>|<num>
Garbage collector output (like a log of all reclaimed memory blocks)
is sent to <filename> (or inherited fd <num>) instead of stderr.
Only available if compiled with MALLOC_smalloc.
--y|--yydebug
Enable debugging of the LPC compiler.
Only available if compiled with YYDEBUG.
--random-seed <num>
Seed value for the random number generator. If not given, the
driver chooses a seed value on its own.
This option is for debugging.
--check-state <lvl>
Perform a regular simplistic check of the virtual machine according
to <lvl>:
= 0: no check
= 1: once per backend loop
= 2: at various points in the backend loop
Only available if compiled with DEBUG.
--check-refcounts
Every backend cycle, all refcounts in the system are checked.
SLOW! Only available if compiled with DEBUG.
--gobble-descriptors <num>
<num> (more) filedescriptors are used up. You'll know when you need it.
Only available if compiled with DEBUG.
--check-strings
Every backend cycle, all shared strings in the system are checked.
SLOW! Only available if compiled with DEBUG and CHECK_STRINGS.
-V|--version
Print the version of the driver and exit.
--options
Print the version and compilation options of the driver and exit.
-h|-?|--help
Display a command help and exit.
--longhelp
Display a long command help and exit.
--args <filename>
The driver reads and parses the given file and treats its contents
as if given on the commandline right where the --args option
occured. The file itself can again contain --args options.
DESCRIPTION -- Argument Parser
The parser analyses the commandline arguments given with the driver
invocation and distinguishes 'options', which start with a '-', from
proper arguments. Options are further distinguished by their name and
may take an additional value. In general, options and arguments can be
givein in any order.
Options are recognized in two forms. In the short form the option must
be given as a single '-' followed by a single letter. In the long form,
options start with '--' followed by a string of arbitrary length. The
short options are case sensitive, the long options aren't.
Most options can be specified in both the short and long form, but that
is not mandatory. Examples: '-r' and '--recursive'.
If an option takes a value, it must follow the option immediately after
a separating space or '='. Additionally, the value for a short option
may follow the option without separator. Examples are: '-fMakefile',
'-f Makefile', '--file=Makefile' and '--file Makefile'.
Short options may be collated into one argument, e.g. '-rtl', but
of these only the last may take a value.
The option '--' marks the end of options. All following command arguments
are considered proper arguments even if they start with a '-' or '--'.
The arguments are usually taken from the commandline; but the parser
is also able to read them from a textfiles, which can be nested. The
content of the textfiles is broken down into words delimited by whitespace,
which are then treated as given on the commandline at the place where
the instruction to read the textfile stood.
The file parser recognizes simple double-quoted strings, which must be
contained on a single line. Additionally, the '#' character given by
itself is a comment marker - everthing after the '#' until the end
of the current line is ignored.
HISTORY
LDMud 3.2.9 added the --max-thread-pending, --hostname,
--hostaddr, --args and --random-seed options.
LDMud 3.2.10 added the --filename-spaces options.
LDMud 3.3.475/3.2.11 added --tls-key, --tls-cert.
LDMud 3.3.672/3.2.11 added --tls-trustfile, --tls-trustdirectory.

12
doc/driver/malloc Normal file
View file

@ -0,0 +1,12 @@
NAME
malloc
DESCRIPTION
This command is hardcoded into the driver's input parser.
It shows the statistics of the memory management module.
HISTORY
Since 3.2.7, 'status malloc' has the same effect.
SEE ALSO
status(D), memory(C), debug_info(E)

13
doc/driver/opcdump Normal file
View file

@ -0,0 +1,13 @@
NAME
opcdump
DESCRIPTION
If the driver was compiled to do opcode profiling, this command
will save the collected profiling information into the file /OPC_DUMP.
If the O_IS_WIZARD flag is used in the mudlib (i.e. if
set_is_wizard() was called), this command is allowed only for
users that have this flag set.
SEE ALSO
malloc(D), status(D), memory(C), objects(C), debug_info(E),
set_is_wizard(E)

95
doc/driver/predefined Normal file
View file

@ -0,0 +1,95 @@
NAME
predefined - predefined #defines by the parser
DESCRIPTION
Several preprocessor macros are pre#defined by the parser,
to provide information about parser version, compile time
options and parser invocation options:
LPC3 : always defined.
__LDMUD__ : always defined.
__EUIDS__ : always (for compatibility).
COMPAT_FLAG : defined if the driver runs in compat mode.
__COMPAT_MODE__ : ditto
__STRICT_EUIDS__: defined if strict euid usage is enforced.
__FILENAME_SPACES__: defined if filenames may contain spaces.
__MASTER_OBJECT__ : the name of the master object (in compat mode
without leading '/').
__FILE__ : the name of the compiled file (in compat mode
without leading '/').
__LINE__ : the actual line number.
__DIR__ : the directory path of the compiled file (in
compat mode without leading '/').
__PATH__(n) : the <n> leading directory path elements of the
compiled file (in compat mode without leading '/').
__VERSION__ : the version string of the driver.
__VERSION_MAJOR__ : the major version number of the driver.
__VERSION_MINOR__ : the minor version number of the driver.
__VERSION_MICRO__ : the micro version number of the driver.
__VERSION_PATCH__ : the patchlevel of the driver; a 0 here means
'no patchlevel'.
__DOMAIN_NAME__ : the domain the host is part of.
__HOST_IP_NUMBER__ : the hosts IP number (as a string).
__HOST_NAME__ : the full hostname.
__MAX_RECURSION__ : the max count of nested function calls
(this is config.h:MAX_USER_TRACE).
__MAX_EVAL_COST__ : the max evaluation cost.
__CATCH_EVAL_COST__ : the min amount of eval cost which needs to
be available to execute a catch().
__MASTER_EVAL_COST__: the eval cost reserve for master applies.
__RESET_TIME__ : default interval time between object resets.
__CLEANUP_TIME__ : default interval time between object cleanups.
__EFUN_DEFINED__(name) : if the efun 'name' exists, this
macro evaluates to " 1 ", else to " 0 ".
__DRIVER_LOG__ : the name of the default debug.log file (within
the mudlib); undefined if a different name
has been specified on the commandline.
__WIZLIST__ : the name of the (mudlib) file from where the
driver read the initial WIZLIST information.
It is undefined if the driver was configured
to not read the information.
__MAX_MALLOC__ : the internal upper limit for total memory
usage.
__INT_MAX__ : the largest integer number
__INT_MIN__ : the smallest integer number
__FLOAT_MAX__ : the largest (positive) float number
__FLOAT_MIN__ : the smallest (positive) float number
If the ERQ is supported, the following macros are defined:
__ERQ_MAX_SEND__ : the max size of the send buffer
__ERQ_MAX_REPLY__ : the max size of the reply buffer
If the driver is compiled for using the MS-DOS filesystem
conventions, __MSDOS_FS__ is defined.
The following macros are defined if their associated package
has been compiled into the driver:
__IPV6__ : support for IP v.6
__MYSQL__ : support for mySQL
__LPC_NOSAVE__: support for the 'nosave' keyword
__LPC_ARRAY_CALLS__: call_other()s on arrays of objects enabled.
__DEPRECATED__: support for obsolete and deprecated efuns.
HISTORY
3.2.1 added __DOMAIN_NAME__, __HOST_IP_NUMBER__, __HOST_NAME__,
__MAX_RECURSION__, __EFUN_DEFINED__().
3.2.5 added __COMPAT_MODE__, __NATIVE_MODE__, __EUIDS__,
__ERQ_MAX_SEND__ and __ERQ_MAX_REPLY__.
3.2.6 added __MAX_EVAL_COST__.
3.2.7 added __STRICT_EUIDS__ and made __EUIDS__ standard.
3.2.8 added __IPV6__, __LPC_NOSAVE__, __DIR__, __PATH__().
3.2.9 added __LDMUD__, __MYSQL__, __DEPRECATED__, __VERSION_MAJOR__,
__VERSION_MINOR__, __VERSION_MICRO__, __VERSION_PATCH__,
__INT_MAX__, __INT_MIN__, __FLOAT_MIN__, __FLOAT_MAX__,
__CATCH_EVAL_COST__, __MASTER_EVAL_COST__, __RESET_TIME__,
__CLEANUP_TIME__, __DRIVER_LOG__, and __WIZLIST__.
3.2.10 added __MAX_MALLOC__, __MSDOS_FS__, __LPC_ARRAY_CALLS__
and __FILENAME_SPACES__.
SEE ALSO
pragma(LPC), preprocessor(LPC)

35
doc/driver/rxcache.doc Normal file
View file

@ -0,0 +1,35 @@
RxCache - Regular Expression Cache
----------------------------------
Written 1998 by Lars Duening.
Share and Enjoy!@
The rxcache acts as a wrapper around the regcomp() function
in the regexp module. It keeps track of the recently compiled
regular expression and stores both the expression string and
regexp's internal representation. If an expression is found
among the stored ones, the rxcache returns the stored internal
representation compiled earlier.
The interface is through the function regcomp_cache() which
takes the same parameters as regcomp(). However, since the
compiled expression is stored and managed by rxcache, the
caller of regcomp_cache() must not deallocate the compiled
expression after use.
The expressions are stored in a fixed size table, with entries
indexed by a hash over the pattern string. The table holds
RXCACHE_TABLE entries, and every entry can hold RXCACHE_CHAIN
expressions. Upon a hash collision, one of the stored expressions
is removed (and deallocated) using a round-robin selection.
This behaviour may not be optimal, but on the other hand
eventually deallocates expressions which are no longer used.
The two RXCACHE_ values are defined in config.h.
Initial tests showed an acceleration of the regexp _setup_
phase by factor 4. However, since the actual matching is as
fast as before, the net savings will be lower.
To install the rxcache, apply the diff file (use parameter
'-p1' with patch) and reconfigure and recompile the driver.
-- Lars Duening; 17. Apr 1998

View file

@ -0,0 +1,17 @@
NAME
showsmallnewmalloced
DESCRIPTION
This command is hardcoded into the driver.
Shows a list of recently allocated small memory blocks.
If the O_IS_WIZARD flag is used in the mudlib (i.e. if
set_is_wizard() was called), this command is allowed only for
users that have this flag set.
HISTORY
Deactivated in 3.2.7 by default.
SEE ALSO
malloc(D), status(D), memory(C), objects(C), debug_info(E),
set_is_wizard(E)

Some files were not shown because too many files have changed in this diff Show more