FuzzBall MUF Changes
I'm still in the process of hypertexting this document. Until I'm done,
here's the flat ASCII version. -- Telzey
TinyMUCK2.2fb4.0 MUF documentation.
===================================
There are now four levels of MUCKERs in fb4.0. Level zero is a non-mucker.
They cannot use the editor, and MUF programs owned by them run as if
they were level 1 MUCKERs.
Level one MUCKER's are apprentices. Their powers are restricted as they
cannot get information about any object that is not in the same room they
are. ie: OWNER, NAME, LOCATION, etc all fail if the object isn't in the
same room as the player. Level one MUCKER programs always run as if they
are set SETUID. NOTIFY, NOTIFY_EXCEPT, and NOTIFY_EXCLUDE will refuse to
send messages to rooms the user is not in. Level one programs also cannot
use ADDPENNIES.
Level two MUCKERs are also called Journeymen. Their permissions are
equivalent to the permissions for a normal MUCKER under older versions
of the server.
Level three MUCKERs are referred to as Masters. They can use the con-
nection info primitives (ie: CONDBREF, ONLINE, etc.), read the EXITS list
of any room, use NEXTOBJ on objects, can use NEWROOM, NEWOBJECT, NEWEXIT,
and COPYOBJ without limitations, can use QUEUE and KILL, and can override
the permissions restrictions of MOVETO. You only give a player MUCKER
level 3 if they are very trusted.
A player who is wizbitted is effectively Mucker Level 4. MUCKER level
four is required for the RECYCLE primitive, the CONHOST primitive, the
FORCE primitive, and the SETOWN primitive. ML4 also allows overriding
of permissions of the SET* primitives, and property permissions. Props
not listed by NEXTPROP with ML3 are listed with ML4.
The MUCKER level permissions that a program runs at is the lesser of
it's own MUCKER level and the MUCKER level of it's owner.
If it is owned by a player who is MUCKER level 2, and it is MUCKER
level 3, then it runs at Muckr level 2. The one exception to this is
programs owned by a Wizard player. They run at Mucker level 2 if the
program itself is not wizbit, and at Mucker level 4 if the program IS
set wizbit.
Mucker level is referred to in flags lists by M# where the # is the
Mucker level. Level zero objects don't show a flag for it. Example:
Revar(#37PM3)
In verbose flags lists, Mucker levels greater than zero are shown
by MUCKER# where # is the mucker level.
To set a level on a player or program, use the level number as the
flag name. MUCKER is the same as 2, and !MUCKER is the same as 0.
Example: @set Revar=2
A player may set the MUCKER level on a program they own to any level
lower than or equal to their own level, and a wizard may set a program
or player to any MUCKER level. A program cannot be set to Mucker Level
Zero, since it has no meaning for programs. (You expect them to use the
MUF editor or something?)
When a program is created, it is automatically set to the same MUCKER
level as the creating player. When a program is loaded from the db, if
it is Mucker Level 0, it is upgraded to Mucker Level 2.
Compiler directives for MUF:
$define <defname> <definition> $enddef
Basically the same as C's #define <defname> <definition>
$undef <defname>
About the same as C's #undef <defname>
$echo <string>
Echos the given string to the screen of the person compiling the program.
Runs at compile-time.
__version
A pre$defined macro that contains the current server version.
Currently "Muck2.2fb3.4"
$ifdef <condition> <compile this if true> $else <compile if false> $endif
$ifndef <condition> <compile this if true> $else <compile if false> $endif
where <condition> is either a $defined name, or a test that consists of
a $defined name, a comparator (=, <, or >) and a test value, all in one
word without space. The $else clause is optional. Compiler directives
are nestable also. Some examples:
$ifndef __version>Muck2.2fb3.5 $define envprop .envprop $enddef $endif
$define ploc $ifdef proplocs .proploc $else $endif $enddef
$include <dbref|progreg>
Sets a bunch of $defines from the properties in the /_defs/ propdir.
For example, if object #345 had the following properties:
/_defs/desc: "_/de" getpropstr
/_defs/setpropstr: dup if 0 addprop else pop remove_prop then
/_defs/setpropval: dup if "" swap addprop else pop remove_prop then
/_defs/setprop: dup int? if setpropval else setpropstr then
then if a program contained '$include #345' in it, then all subsequent
references to 'desc', 'setpropstr', 'setpropval', and 'setprop' would
be expanded to the string values of their respective programs. ie:
'desc' would be replaced throughout the program with '"_/de" getpropstr'
You can now escape a token in MUF so that it will be interpreted literally.
ie: \.pmatch will try to compile '.pmatch' without expanding it as a
macro. This lets you make special things with $defines such as:
$define addprop over over or if \addprop else pop pop remove_prop $enddef
so that all the 'addprop's in the program will be expanded to the
definition, but the 'addprop' in the definition will not try to expand
recursively. It will call the actual addprop.
Multitasking:
There are now 3 modes that a program can be in when running: foreground,
background, and preempt. A program running in the foreground lets other
users and programs have timeslices (ie multitasking), but blocks input
from the program user. Background mode allows the user to also go on and
do other things and issue other commands, but will not allow the program
to do READs. Preempt mode means that the program runs to completion
without multitasking, taking full control of the interpreter and not
letting other users or progs have timeslices, but imposes an instruction
count limit unless the program is a wizard program.
Programs run by @locks, @descs, @succs, @fails, and @drops default to the
preempt mode when they run. Programs run by actions linked to them
default to running in foreground mode. QUEUEd program events, such as
those set up by _listen, _connect, _disconnect, etc, and those QUEUEd by
other programs default to running in background mode. (NOTE: these
programs cannot be changed out of background mode)
New primitives:
PUBLIC <functionname> Declares a function to be public for execution by
other programs. This is a compile-time directive,
not a run-time primitive. To call a public
function, put the dbref of the program on the
stack, then put a string, containing the function
name, on the stack, then use CALL. For example:
#888 "functionname" CALL
LVAR <varname> This declares a variable as a local variable, that is
local to a specific program. If another program calls
this program, the values of the local variables will not
be changed in the calling program, even if the called
program changes them.
LOCALVAR (i -- l) Takes an integer and returns the respective local
variable. Similar to the 'variable' primitive.
ABORT (s -- ) Halts the interpreter with an error of the string given.
SETLINK (d d -- ) Takes an exit dbref and a destination dbref and sets
the exit link to that destination. Note that if the
exit is already linked, it must be unlinked by doing
a setlink with #-1 as a destination.
SETOWN (d d -- ) Sets the ownership of the first object to the player
given in the second dbref. (wizbit only)
NEWROOM (d s -- d) Takes dbref of the location, and the name in a string.
Returns the dbref of the room created. Owner is
runner of the program. (Mucker Level 3)
NEWOBJECT (d s -- d) Takes location and name and returns new thing's dbref.
(Mucker Level 3)
NEWEXIT (d s -- d) Takes location and name and returns new exit's dbref.
(Requires Mucker Level 3)
RECYCLE (d -- ) Recycles the given object d. Will not recycle
players, the global environment, the player
starting room, or any currently running program.
(Can recycle objets owned by uid if running with
Mucker Level 3 permissions. Can recycle other
people's items with wizbit)
STATS (d -- 7 ints) Takes a dbref and returns 7 integers, giving, in order,
The total number of objects owned by the given dbref,
the number of rooms owned, the number of exits owned,
the number of things owned, number of programs owned,
number of players, and number of garbage items owned.
If the given dbref was #-1, then the stats are the
totals for the entire database. (Needs Mucker Level 3)
AWAKE? (d -- i) Returns whether the given player (dbref) is connected.
It returns how many times they are connected.
ONLINE ( -- d d..d i) Returns the dbrefs of all the players logged in,
with the count of them on the top of the stack.
(Requires Mucker Level 3)
CONCOUNT ( -- i) Returns how many connections to the server there are.
(Requires Mucker Level 3)
CONDBREF (i -- d) Returns the dbref of the player connected to this
connection. (Requires Mucker Level 3)
CONTIME (i -- i) Returns how many seconds the given connection has been
connected to the server. (Requires Mucker Level 3)
CONIDLE (i -- i) Returns how many seconds the connection has been idle.
(Requires Mucker Level 3)
CONDESCR ( i -- i ) Takes a connection number and returns the descriptor
number associated with it. (Requires Mucker Level 3)
DESCRCON (i -- i) Takes a descriptor and returns the associated connection
number, or 0 if no match was found.
DESCRIPTORS (d -- ix...i1 i) Takes a player dbref, or #-1, and returns the
range of descriptor numbers associated with
that dbref (or all for #-1) with their count
on top.
CONHOST (i -- s) Returns the hostname of the connection. (wizbit only)
CONBOOT (i -- ) Takes a connection number and disconnects that
connection from the server. Basically @boot for
a specific connection. (wizbit only)
CONNOTIFY (i s -- ) Sends a string to a specific connection to the
server. (Requires Mucker Level 3)
INT? (? -- i) Tells whether the top stack item is an integer.
STRING? (? -- i) Tells whether the top stack item is a string.
DBREF? (? -- i) Tells whether the top stack item is a dbref.
NEXTPROP (d s -- s) This takes a dbref and a string that is the name of a
property and returns the next property name on that
dbref, or returns a null string if that was the last.
To *start* the search, give it a propdir, or a blank
string. For example, '#10 "/" NEXTPROP' or
'#28 "/letters/" NEXTPROP' A blank string is the
same as "/". (Requires Mucker Level 3)
The NEXTPROP primitive will skip over property names that the program
and user would not have permissions to read. Wizbit programs can see all
of the properties on an object. Otherwise, nextprop will skip over the
props that are not readable by the uid the program is running under.
PROPDIR? (d s -- i) Takes a dbref and a property name, and returns whether
that property is a propdir that contains other props.
(Requires Mucker Level 3)
ENVPROPSTR (s d -- s d ) Takes a starting object dbref and a property name
and searches down the environment tree from that
object for a property with the given name.
If the property isn't found, it returns #-1 and
a null string. If the property is found, it will
return the dbref of the object it was found on,
and the string value it contained.
NOTIFY_EXCLUDE (d dn ... d1 n s -- ) Displays the message s to all the
players (or _listening objects), excluding the
n given players, in the given room. Example:
#0 #1 #23 #7 3 "Hi!" notify_exclude
would send "Hi!" to everyone in room #0 except
for players (or objects) #1, #7, and #23.
NOTIFY_EXCEPT has been removed and replaced by a $define to NOTIFY_EXCLUDE.
It is effectively $define notify_except 1 swap notify_exclude $enddef
NOTIFY and NOTIFY_EXCLUDE will not trigger _listen's that would run the
current program.
SYSTIME ( -- i) Returns the number of seconds since 1/1/79 00:00 GMT
TIMEFMT (s i -- s) Takes a format string and a SYSTIME integer and returns
a string formatted with the time. The format string
is ascii text with formatting commands:
%% -- "%"
%a -- abbreviated weekday name.
%A -- full weekday name.
%b -- abbreviated month name.
%B -- full month name.
%C -- "%A %B %e, %Y"
%c -- "%x %X"
%D -- "%m/%d/%y"
%d -- month day, "01" - "31"
%e -- month day, " 1" - "31"
%h -- "%b"
%H -- hour, "00" - "23"
%I -- hour, "01" - "12"
%j -- year day, "001" - "366"
%k -- hour, " 0" - "23"
%l -- hour, " 1" - "12"
%M -- minute, "00" - "59"
%m -- month, "01" - "12"
%p -- "AM" or "PM"
%R -- "%H:%M"
%r -- "%I:%M:%S %p"
%S -- seconds, "00" - "59"
%T -- "%H:%M:%S"
%U -- week number of the year. "00" - "52"
%w -- week day number, "0" - "6"
%W -- week# of year, starting on a monday,
"00" - "52"
%X -- "%H:%M:%S"
%x -- "%m/%d/%y"
%y -- year, "00" - "99"
%Y -- year, "1900" - "2155"
%Z -- Time zone. "GMT", "EDT", "PST", etc.
TIMESPLIT (i -- i i i i i i i i) Takes a systime integer, and splits it up
into second, minute, hour, dayofmonth, month, year,
dayofweek, and dayofyear. 1 == sunday for dayofweek.
Dayofyear is a number from 1 to 366.
TIMESTAMPS ( d -- i i i i ) Takes the dbref of an object, and returns
the four timestamp ints in the order of
Created, Modified, Lastused, and Usecount
where the three times are in systime
notation.
SLEEP (i -- ) makes the program pause here for 'i' seconds.
QUEUE (i d s -- i) Takes a time in seconds, a program's dbref, and a
parameter string. It will execute the given program
with the given string as the only string on the
stack, after a delay of the given number of second.
Returns pid of the queued process. QUEUE'd
programs cannot use the READ primitive. They are
writeonly. When a QUEUEd program executes, it does
so with me @ set to the player who was running the
program that QUEUEd it, a loc @ of #-1, and a
trigger @ of #-1.
(Requires Mucker Level 3)
FORK ( -- i) This primitive forks off a BACKGROUND (muf) process from
the currently running program. It returns the pid of
the child process to the parent process, and returns a
0 to the child. This does NOT do a UNIX fork. It only
copies the current stack frame and puts the background
muf process on the time queue. (Requires Mucker Level 3)
KILL (i -- i) Attempts to kill the given process number. Returns 1
if the process existed, and 0 if it didn't.
(Requires Mucker Level 3)
ISPID? (i -- i) Takes a process id and checks to see if an event with that
pid is in the timequeue. It returns 1 if it is, and 0 if
it is not. NOTE: since the program that is running is not
on the timequeue WHILE it is executing, but only when it
is swapped out letting other programs run, 'pid ispid?'
will always return 0.
TOUPPER (s -- s) Takes a string and returns it with all the letters
in uppercase.
TOLOWER (s -- s) Takes a string and returns it with all the letters
in lowercase.
STRIPLEAD (s -- s) Strips leading spaces from the given string.
STRIPTAIL (s -- s) Strips trailing spaces from the given string.
STRIP (s -- s) This is a built in $define. It is read as
"striplead striptail"
SMATCH ( s s -- i ) Takes a string and a string pattern to check against.
Returns true if the string fits the pattern. In the
pattern string, a '?' matches any single character,
'*' matches any number of characters. Word matching
can be done with '{word1|word2|etc}'. If multiple
characters are in [], it will match a single
character that is in that set of characters.
'[aeiou]' will match a single character if it is a
vowel, for example. To search for one of these
special chars, put a \ in front of it to escape it.
It is not case sensitive. Example pattern:
"{Foxen|Lynx|Fiera} *t[iy]ckle*\?" Will match any
string starting with 'Foxen', 'Lynx', or 'Fiera',
that contains either 'tickle' or 'tyckle' and ends
with a '?'.
SETLOCKSTR (d s -- i) Tries to set the lock on the given object to the
lock expression given in the string. If it was a
success, then it will return a 1, otherwise, if
the lock expression was bad, it returns a 0. To
unlock an object, set its lock to a null string.
GETLOCKSTR ( d -- s ) Returns the lock expression for the given object
in the form of a string. Returns "*UNLOCKED*" if
the object doesn't have a lock set.
LOCKED? (d d -- i) Takes, in order, the dbref of the object to test the
lock on, and the dbref of the player to test the lock
against. It tests the lock, running programs as
necessary, and returns a integer of 0 if it is not
locked against them, or 1 if it is.
UNPARSEOBJ ( d -- s ) Returns the name-and-flag string for an object.
For example: "One(#1PW)"
DBTOP ( -- d) Returns the dbref of the first object beyond the top
object of the database.
DEPTH ( -- i) Returns how many items are on the stack.
VERSION ( -- s) Returns the version of this code in a string.
"Muck2.2fb4.0" is the current version.
PROG ( -- d) Returns the dbref of the currently running program.
TRIG ( -- d) Returns the dbref of the original trigger.
CALLER ( -- d) Returns the dbref of the program that called this one,
or the dbref of the trigger, if this wasn't called
by a program.
BITOR (i i -- i) Does a logical bitwise or.
BITXOR (i i -- i) Does a logical bitwise exclusive or.
BITAND (i i -- i) Does a logical bitwise and.
BITSHIFT (i i -- i) Shifts the first integer by the second integer's
number of bit positions. Same as the C << operator.
If the second integer is negative, its like >>.
FORCE (d s -- ) Forces player d to do action s as if they were
@forced. (wizbit only)
PREEMPT ( -- ) Prevents a program from being swapped out to do
multitasking. Needed in some cases to protect
crutial data from being changed while it is being
worked on. A program will remain in preempt mode
until it's execution is completed. Basically what
this command does is to turn off multitasking, but
then you have a limit on how many instructions you
can run without needing either to pause with a SLEEP,
or have a wizbit on the program.
FOREGROUND ( -- ) To turn on multitasking, you can issue a foreground
command. While a program is in foreground mode, the
server will be multitasking and handling multiple
programs at once, and input from other users, but it
will be blocking any input from the user of the program
until the program finishes. You cannot foreground a
program once it is running in the background. A program
will stay in foreground mode until it finishes running
or until you change the mode.
BACKGROUND ( -- ) Another way to turn on multitasking is to use the
background command. Programs in the background let
the program user go on and be able to do other things
while waiting for the program to finish. You cannot
use the READ command in a background program. Once a
program is put into background mode, you cannot set
it into foreground or preempt mode. A program will
remain in the background until it finishes execution.
BEGIN ( -- ) Marks the beginning of begin-until or begin-repeat loops
UNTIL (i -- ) If the value on top of the stack is false, then it jumps
execution back to the instruction afer the matching
BEGIN statement. (BEGIN-UNTIL, BEGIN-REPEAT, and
IF-ELSE-THEN's can all be nested as much as you want.)
If the value is true, it exits the loop, and executes
the next instruction, following the UNTIL. Marks the
end of the current loop.
REPEAT ( -- ) Jumps execution to the instruction after the BEGIN in a
BEGIN-REPEAT loop. Marks the end of the current loop.
WHILE (i -- ) If the value on top of the stack is false, then this
causes execution to jump to the instruction after the
UNTIL or REPEAT for the current loop. If the value is
true, however, execution falls through to the instr-
uction after the WHILE.
BREAK ( -- ) Breaks out of the innermost loop. Jumps execution to
the instruction after the UNTIL or REPEAT for the
current loop.
CONTINUE ( -- ) Jumps execution to the beginning of the current loop.
The BEGIN statement marks the beginning of a loop.
Either the UNTIL or the REPEAT statement marks the end of the loop.
REPEAT will do an unconditional jump to the statement after the BEGIN
statement.
UNTIL checks to see if the value on the stack is false. If it is, it
jumps execution to the statement after the BEGIN statement, otherwise,
it falls through on execution to the statement after the UNTIL.
Within a loop, even within IF-ELSE-THEN structures within the loop
structure, you can place WHILE, CONTINUE, or BREAK statements. There
is no limit as to how many, or in what combinations these instructions
are used.
A WHILE statement checks to see if the value on the stack is false.
If it is, execution jumps to the first statement after the end of
the loop. If the value was true, execution falls through to the
statement after the WHILE.
The CONTINUE statement forces execution to jump to the beginning of
the loop, after the BEGIN.
The BREAK statement forces execution to jump to the end of the loop,
at the statement after the REPEAT or UNTIL, effectively exiting the
loop.
Note: You can nest loops complexly, but WHILE, BREAK, and CONTINUE
statements only refer to the innermost loop structure.
Example of a complex loop structure:
101 begin (BEGIN the outer loop)
dup while 1 - (This WHILE, ...)
dup not if break then (this BREAK, and..)
dup 2 % not if continue then (this CONTINUE refer to the outer loop)
dup 10 % not if
15 begin (BEGIN inner loop)
dup while 1 - (This WHILE, and.. )
dup 5 % not if break then (... this BREAK, refer to inner loop)
repeat (This REPEAT statement ends inner loop.)
then
dup 7 % not if continue then (This CONTINUE, and...)
dup 3 % not if dup 9 % while then (this WHILE refer to the outer loop)
dup intostr me @ swap notify
dup 1 = until pop (This UNTIL ends the outer loop)
On dbload, if a program is set ABODE (AUTOSTART), *AND* it is owned by
a wizard, then it will be placed in the timequeue with a delay of 0 and
a string parm of "Startup". Autostart programs run with the location
NOTHING (#-1) rather than the location of the owner of the program.
You can now check the Interactive flag on a player in MUF to see if they
are in READ mode or @edit'ing.
MOVETO will now run programs in the @desc and @succ/@fail of a room when
moving a player.
When a message is notify_except'ed or notify_exclud'ed to a room, and
LISTENERS and LISTENERS_ENV are defined, then it will run ALL the
programs referred to in all the _listen properties down the environment
tree. Also, the muf NOTIFY primitive was changed to run the _listen
program on an object or player if a message is sent to them that way.
Removed DESC, SUCC, FAIL, DROP, OSUCC, OFAIL, ODROP, SETDESC, SETSUCC,
SETFAIL, SETDROP, SETOSUCC, SETOFAIL, and SETODROP and replaced them with
$defines that would set/get the appropriate information in/from properties.
For example, DESC is now translated to '"_/de" getpropstr' and SETDESC is
translated to '"_/de" swap 0 addprop'. One result of this is that if you
tried to escape one of these command names with a backslash in a macro
definition, it wouldn't work. ie: \SETDESC would get a compile error.
There is a COMMAND variable, similar to ME, LOC, and TRIGGER, except that
it contains a string. The string contains the command the user typed
that triggered the the program, without the command line arguments. ie:
if there was an exit named "abracadabra;foo bar;frozzboz" that was linked
to the program, and the user typed in "foo bar baz", then the program
would run with "baz" on the stack, and "foo bar" in the global COMMAND
variable.
Made ROOM?, PLAYER?, THING?, EXIT?, and PROGRAM? primitives return false
if the object passed to it is invalid, instead of aborting interpreting.
INSTRING and RINSTRING are now inserver defines that let you do case
insensitive versions of their instr and rinstr counterparts.
Programs are now compiled when they are run or called instead of when
the databate is loaded. They are compiled with the uid of the owner
of the program.
If a program has the HAVEN flag set on it (HARDUID) then it runs with
the uid and permissions of the owner of the trigger of the program.
If the program is a timequeue event (with trigger of #-1), then it
will run with the permissions and uid of the program owner as in SETUID.
A room or player may have a "_connect" property set that contains the
dbref of a progran to run when a player connects. The program must be
either link_ok or must be owned by the player connecting. When the
program is run, the string on the stack will be "Connect", the "loc @"
will be the location of the connecting player, the "me @" will be the
connecting player, and the "trigger @" (and "trig") will be #-1. All
programs referred to by _connect properties on the player, and on rooms
down the environment tree from the player, will be QUEUEd up to run.
When a player desconnects, programs referred to by _disconnect properties
will be run in a similar manner.
(connect and disconnect _actions_ are also implemented.)
Programs refered to by props in _depart/_arrive/_connect/_disconnect propdirs
will now be all queued up, eliminating the need for a dispatcher program.
An example would be _connect/announce:1234 That would queue up program
#1234 when a player connects. The name ("announce") is not important, and
can be anything you want, but they are executed in alphabetic order.
Programs set BUILDER (BLOCKED) run in preempt mode, regardless of the mode
of the program. ie: a foreground program, while running in a program set
BLOCKED, will run pre-empt, with the multitasking effectively shut off.
Programs set HARDUID and SETUID, that are owned by a Wizard, run at the same
mucker level as the calling program. This is useful for writing libraries.
Tabs sent to the server are now interpreted as single spaces to help solve
a problem with tabbed comments in MUF programs being uploaded.
Return to the TinyMUCK Page
Page created by Telzey, and maintained by Tugrik d'Itichi.
Comments/Questions/Flames
to: FMPages@furry.com