mirror of
https://github.com/ldmud/ldmud
synced 2026-08-12 14:26:05 -04:00
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:
commit
77996da5fc
1602 changed files with 278250 additions and 0 deletions
481
COPYING.LIB
Normal file
481
COPYING.LIB
Normal 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
57
COPYRIGHT
Normal 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
262
CREDITS
Normal 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)
|
||||
298
INSTALL
Normal file
298
INSTALL
Normal 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
27
README
Normal 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
67
doc/3.2vs3.2.1
Normal 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
35
doc/FAQ
Normal 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
50
doc/LPC/alists
Normal 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
116
doc/LPC/arrays
Normal 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
46
doc/LPC/block
Normal 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
730
doc/LPC/closure_guide
Normal 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
506
doc/LPC/closures
Normal 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
80
doc/LPC/closures-abstract
Normal 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
202
doc/LPC/closures-example
Normal 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
44
doc/LPC/comments
Normal 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
15
doc/LPC/do-while
Normal 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
39
doc/LPC/ed0
Normal 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
32
doc/LPC/ed1
Normal 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
25
doc/LPC/ed2
Normal 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
30
doc/LPC/ed3
Normal 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
12
doc/LPC/ed4
Normal 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
47
doc/LPC/ed5
Normal 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
32
doc/LPC/ed6
Normal 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
16
doc/LPC/efuns
Normal 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
81
doc/LPC/escape
Normal 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
36
doc/LPC/for
Normal 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
71
doc/LPC/foreach
Normal 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
160
doc/LPC/functions
Normal 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
39
doc/LPC/if
Normal 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
9
doc/LPC/inherit
Normal 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
38
doc/LPC/initialisation
Normal 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
31
doc/LPC/lfuns
Normal 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
9
doc/LPC/lpc
Normal 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
450
doc/LPC/mappings
Normal 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
177
doc/LPC/modifiers
Normal 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
203
doc/LPC/operators
Normal 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
107
doc/LPC/pragma
Normal 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
23
doc/LPC/preprocessor
Normal 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
49
doc/LPC/references
Normal 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
90
doc/LPC/switch
Normal 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
134
doc/LPC/types
Normal 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
72
doc/LPC/varargs
Normal 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
15
doc/LPC/while
Normal 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
18
doc/Makefile
Normal 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
89
doc/README
Normal 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
1
doc/TODO
Normal file
|
|
@ -0,0 +1 @@
|
|||
The concepts/ LPC/ and driver/ still need to be worked at.
|
||||
12
doc/applied/__INIT
Normal file
12
doc/applied/__INIT
Normal 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
20
doc/applied/add_weight
Normal 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
12
doc/applied/applied
Normal 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)
|
||||
16
doc/applied/can_put_and_get
Normal file
16
doc/applied/can_put_and_get
Normal 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
17
doc/applied/catch_msg
Normal 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
21
doc/applied/catch_tell
Normal 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
51
doc/applied/clean_up
Normal 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
24
doc/applied/create
Normal 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
21
doc/applied/drop
Normal 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
18
doc/applied/exit
Normal 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
14
doc/applied/get
Normal 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
40
doc/applied/heart_beat
Normal 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
15
doc/applied/id
Normal 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
56
doc/applied/init
Normal 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
21
doc/applied/logon
Normal 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)
|
||||
27
doc/applied/modify_command
Normal file
27
doc/applied/modify_command
Normal 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)
|
||||
13
doc/applied/parse_command_adjectiv_id_list
Normal file
13
doc/applied/parse_command_adjectiv_id_list
Normal 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)
|
||||
8
doc/applied/parse_command_id_list
Normal file
8
doc/applied/parse_command_id_list
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
SYNOPSIS
|
||||
string *parse_command_id_list(void)
|
||||
|
||||
DESCRIPTION
|
||||
Used by parse_command().
|
||||
|
||||
SEE ALSO
|
||||
parse_command(E)
|
||||
8
doc/applied/parse_command_plural_id_list
Normal file
8
doc/applied/parse_command_plural_id_list
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
SYNOPSIS
|
||||
string *parse_command_plural_id_list(void)
|
||||
|
||||
DESCRIPTION
|
||||
Used by parse_command().
|
||||
|
||||
SEE ALSO
|
||||
parse_command(E)
|
||||
15
doc/applied/prevent_insert
Normal file
15
doc/applied/prevent_insert
Normal 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
9
doc/applied/query_weight
Normal 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
21
doc/applied/remove
Normal 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
32
doc/applied/reset
Normal 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
9
doc/concepts/concepts
Normal 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
559
doc/concepts/erq
Normal 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
16
doc/concepts/files
Normal 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
505
doc/concepts/hooks
Normal 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
63
doc/concepts/imp
Normal 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
176
doc/concepts/inheritance
Normal 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
224
doc/concepts/intermud
Normal 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
151
doc/concepts/intermud.basic
Normal 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
100
doc/concepts/mccp
Normal 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
56
doc/concepts/memory
Normal 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
205
doc/concepts/mysql
Normal 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
251
doc/concepts/native
Normal 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
316
doc/concepts/negotiation
Normal 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
25
doc/concepts/objects
Normal 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
33
doc/concepts/rtfm
Normal 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
14
doc/concepts/simul_efun
Normal 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
73
doc/concepts/tls
Normal 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
96
doc/concepts/uids
Normal 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
241
doc/driver/codestyle
Normal 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
252
doc/driver/copyright-issue
Normal 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
17
doc/driver/debugmalloc
Normal 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
15
doc/driver/driver
Normal 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
49
doc/driver/dumpallobj
Normal 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
307
doc/driver/invocation
Normal 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
12
doc/driver/malloc
Normal 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
13
doc/driver/opcdump
Normal 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
95
doc/driver/predefined
Normal 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
35
doc/driver/rxcache.doc
Normal 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
|
||||
17
doc/driver/showsmallnewmalloced
Normal file
17
doc/driver/showsmallnewmalloced
Normal 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
Loading…
Add table
Add a link
Reference in a new issue