message_doc
the message() efun
message() is the FluffOS efun designed to make communication efuns more
generic. It attaches a classification (the message type) to every message so
that recipients can decide how to handle it, which in turn makes it
straightforward to communicate intelligently with capable clients.
Here is the manual page for message():
void message(mixed type, mixed message,
string | string * | object | object * target,
void | object | object * exclude);
message() calls receive_message(mixed type, mixed message) in all message
recipients (derived from the target list) excluding those in the exclude list.
This basically tells the objects the message through the receive_message apply.
The exclude argument is optional.
type is an arbitrary classification for the message. The driver does not
interpret it or forward it to the client on its own; it is simply passed along
to receive_message in each recipient, where your LPC code decides what to do
with it — for example, filtering out certain types, or forwarding it to a client
as part of some structured communication protocol. An example would be 'combat',
'shout', 'emergency', 'system', 'room description', etc.
message is the message to be sent, normally a string.
target is a list of objects to be sent the message. This can be either a
single object string or object pointer, or may be an array of either.
If a target is non-living, all objects in its environment will receive
the message.
exclude is a list of objects that should not receive the message. This
can be a single object pointer or an array of object pointers.
The most important element of this function is the type. Because every message
sent through message() carries one, the recipient's receive_message can
decide what to do per message — display it, discard it, filter by category, or
hand it to a client.
Note that this classification applies only to content sent via message(). The
traditional output efuns — say(), tell_object(), shout(), write(),
tell_room() — deliver their text through the catch_tell apply instead and
never reach receive_message. A mudlib that wants all of its output to be
classifiable typically routes that output through message() (often behind
simul_efuns), choosing whatever type names suit it, such as "shout", "say",
"tell", or "emote".
An example would be overriding the shout() efun in your simul_efun object
to use message():
void shout(string msg) {
object *exclude = ({});
if(this_player())
exclude += ({ this_player() });
message("shout", msg, users(), exclude);
}
Note the exclusion of this_player(). The built-in shout() and say() efuns
never echo back to the object performing the action (the command giver), so an
override that reproduces their behavior must keep that semantic and exclude
this_player() from the recipients.
Given this, let's say that you wanted to implement a quick and easy
earmuff ability (the ability to mask shouts). In your user (player)
object, you would have the function receive_message. Here's the
simplest implementation possible:
void receive_message(string type, string msg) {
receive(msg);
}
This simply takes all messages generated by the message() efun and
displays them to the user. However, you could imagine a simple
earmuffs implementation on top of this:
string *muffled = ({});
void muffle_type (string type)
{
muffled += ({ type });
}
void receive_message (string type, string msg)
{
if (member_array(type, muffled) == -1) {
receive(msg);
}
}
Now you can see that if a particular type is muffled (say, "shout" for example), the text never gets displayed, but for other types it does.
A single coarse type is rarely enough. Suppose a player muffles the "shout"
type, but an admin needs to announce that the system is going down in five
minutes. If that announcement is also sent (via message()) as type "shout",
the player misses it. This is why a mudlib usually defines a broader set of
types — for example a dedicated "broadcast" type for important announcements
that everyone should see, perhaps one that muffling is not permitted to block.
You can layer convenience simul_efuns on top of message() so that
writing content is as easy as the traditional write()/say() efuns
while still classifying every message. For example, an emote()
simul_efun that all soul commands use, passing the "emote" type, might
look like:
varargs int emote(object emoter, string self_message,
string other_message, mixed emotee,
string target_message, string modifier);
// emoter - the object doing the emoting
// self_message - the message displayed to the emoter
// other_message - the message displayed to the whole room
// emotee - the target of the emote (i.e. kick huthar)
// target_message - the message displayed to the emotee
// modifier - any extra modifier to tack on to the end of the
// emote string (i.e. adverbs: smiles happily,
// cheerfully, etc.) — only really complex soul
// commands need this
Talking to clients
Selective muffling is just one simple thing the type classification makes
possible. The larger payoff comes when the type is used to drive an out-of-band
client protocol: rather than only displaying the text, receive_message can
forward structured data to a capable client so it can route room descriptions,
conversation, combat, and status into separate windows or a status line.
When this document was originally written, no such standard existed, and it went
on to propose an ad-hoc "type:length:msg" text convention for a hypothetical
"smart client". That role is now filled by standardized out-of-band protocols
that FluffOS supports natively; use these rather than inventing your own wire
format:
- GMCP — Generic MUD Communication Protocol
- MSDP — Mud Server Data Protocol
- MXP — MUD eXtension Protocol
- MSP — MUD Sound Protocol
- ZMP — Zenith MUD Protocol
A typical pattern is to keep using message() with a meaningful type for
in-game classification, and have receive_message (or the relevant protocol
apply) hand a structured payload to the client through the appropriate efun,
such as send_gmcp().