跳到主要内容

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().