fluffos/docs/concepts/general/message_doc.md
Yucong Sun 89060c5a6b
docs: fully-expandable generated sidebar + Chinese docs via Docusaurus i18n (#1246)
* docs: fully-expandable generated sidebar, replacing index.md link pages

Rework docs navigation so the sidebar expands to every page of every
reference tree, instead of terminating at generated index.md link lists:

- New docs/gen_sidebar.py (replaces gen_index.py + update_index.sh):
  walks efun/, apply/, stdlib/, concepts/, driver/, cli/ and zh-CN/ and
  emits sidebars.generated.json — a full Docusaurus category tree per
  directory. Category landing pages are now `generated-index` card pages
  (title/description/slug), so all generated index.md files are deleted.
  --check mode verifies freshness; new .github/workflows/docs-sidebar.yml
  runs it in CI.
- New docs/sidebar_meta.json holds curated presentation: category labels,
  one-line descriptions (shown on the landing cards), explicit ordering
  (driver/cli/concepts read top-down from user-facing to internals) and
  per-page label overrides.
- sidebars.ts becomes a hand-authored skeleton (Getting Started, lpc/,
  Historical) that splices in the generated trees.

Content reorganization (from a docs-wide review):
- Move misplaced efun pages out of efun/general: terminal/protocol efuns
  (act_mxp, send_zmp, request_term_*) to interactive/, debugging efuns
  (check_memory, dump_*, clear_debug_level, destructed_objects) to
  internals/, shallow_inherit_list to system/.
- Delete stub duplicates superseded by complete pages elsewhere:
  general/parse_{add_synonym,dump,my_rules,remove}, contrib/{shuffle,
  element_of}.

Modernize key pages with MDX:
- index.mdx: landing page with a card grid linking each doc section.
- build.mdx: per-platform <Tabs> (Ubuntu/macOS/Windows/Alpine+Docker),
  admonitions, VitePress [[toc]] leftover removed, stale per-platform CI
  workflow links updated to the unified ci.yml.
- ffi-plan.md: GitHub-style [!CAUTION] alert converted to an admonition.

`npm run build` passes clean (onBrokenLinks: throw, no warnings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174GM2azvAHBmvyESwxm5om

* docs: serve the Chinese corpus through Docusaurus i18n

Move the zh-CN/ directory out of the default docs tree and into a proper
Docusaurus locale (i18n/zh-CN/docusaurus-plugin-content-docs/current/):

- The flat zh-CN/efun/ directory (333 pages) is re-homed to mirror the
  categorized English layout (name-matched 1:1; `hash` maps to strings/
  per its own frontmatter). apply/ pages map 1:1; the stray English-text
  zh-CN/apply/master/view_errors.md documents a MudOS-era apply that no
  longer exists in the driver and is dropped; stdlib/db/database_zh.md
  becomes the i18n translation of stdlib/db/database.md; the Chinese
  build guide becomes the translation of build.mdx.
- Untranslated pages automatically fall back to English content under
  /zh-CN/, so the whole site is navigable in either locale from the new
  navbar locale dropdown.
- Both locales share one sidebar. Generated sidebar items now carry
  stable `key` fields (the directory/doc path) so translation keys are
  unique (both efun/ and stdlib/ have an "Arrays" category, crypto and
  strings both document `hash`). Category labels, generated-index
  titles/descriptions, navbar and footer are translated in
  i18n/zh-CN/...; theme UI strings come from Docusaurus' bundled
  zh-Hans translations. Translated landing page at /zh-CN/.
- The "中文文档" sidebar section, the zh-CN tree in gen_sidebar.py /
  sidebar_meta.json, and its slice of sidebars.generated.json are gone.
- Relative .md-file links on pages that render in both locales break
  the localized build (the file->permalink map points at the localized
  copy), so concepts/, the two socket_*_option pages and the config.md
  generator now emit extension-less route links instead.
- zh interactive.md/objects.md get explicit slugs like their English
  counterparts (a doc named after its parent directory is otherwise a
  Docusaurus category-index doc, colliding with the generated-index
  route).

`npm run build` builds both locales clean (onBrokenLinks: throw).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174GM2azvAHBmvyESwxm5om

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 17:20:25 -04:00

6.5 KiB

title
general / message_doc

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