TinyMUX

Flag System

Hardcode

Flags are boolean properties stored on every object in the database. They control visibility, permissions, behavior, and status. Examples include WIZARD, DARK, HALT, PUPPET, and CONNECTED. The object type (room, thing, exit, player) is also encoded in the flag words.

Storage: the FLAGSET structure

Each object carries a FLAGSET, defined in flags.h as an array of three unsigned-integer words:

typedef struct flagset {
    FLAG word[3];
} FLAGSET;

The words are indexed by three constants:

ConstantIndexContents
FLAG_WORD10Object type in the low 3 bits, plus core flags such as WIZARD, DARK, HALT, INHERIT, SAFE, ROYALTY.
FLAG_WORD21Extended flags: CONNECTED, ANSI, BLIND, FIXED, GAGGED, STAFF, SLAVE, UNFINDABLE, and others.
FLAG_WORD32Third-generation flags: SITEMON, CMDCHECK, UNICODE, ALONE, NOEXAMINE, NOMODIFY, INDESTRUCTIBLE, NOEVAL, plus ten user-defined marker flags (MARKER0 through MARKER9).

The low three bits of word 1 hold the object type (TYPE_ROOM = 0, TYPE_THING = 1, TYPE_EXIT = 2, TYPE_PLAYER = 3, TYPE_GARBAGE = 5). The macro Typeof(x) extracts them with Flags(x) & TYPE_MASK.

The FLAGSET lives in the per-object structure db[obj].fs. Three convenience macros in db.h provide direct access:

#define Flags(t)   db[t].fs.word[FLAG_WORD1]
#define Flags2(t)  db[t].fs.word[FLAG_WORD2]
#define Flags3(t)  db[t].fs.word[FLAG_WORD3]

Flags are set and cleared with s_Flags(obj, word, value), which writes a new word value and, in the SQLite-backed builds, persists it to the object record.

The flag table

Two C structures describe every flag. FLAGBITENT holds the bit-level definition:

typedef struct flag_bit_entry {
    unsigned int flagvalue;  // bitmask within the word
    UTF8 flaglett;           // single-character letter for display
    int  flagflag;           // which word (FLAG_WORD1/2/3)
    int  listperm;           // who can see the flag when set
    bool (*handler)(...);    // permission handler for set/clear
} FLAGBITENT;

FLAGNAMEENT maps a name string to a FLAGBITENT and adds a sense bit:

typedef struct flag_name_entry {
    const UTF8 *pOrigName;  // canonical name
    bool bPositive;         // true = positive sense, false = negated alias
    FLAGBITENT *fbe;        // pointer to the bit entry
    UTF8 *flagname;         // current display name (may differ after @flag rename)
} FLAGNAMEENT;

The master table gen_flag_names[] in flags.cpp lists every built-in flag and alias. At startup, init_flagtab() loads these entries into a hash map for O(1) lookup.

Flag aliases and negated names

Several names in the table are aliases that map to the same underlying bit. Negated aliases have bPositive set to false; they test or set the absence of the bit. Examples from the source:

AliasUnderlying flagSense
BLEEDNOBLEEDnegated
SPOOFNOSPOOFnegated
COMMANDSNO_COMMANDnegated
ACCENTSASCIInegated
NOEXAMINE / NO_EXAMINEsame NOEXAMINE bitboth positive, two spellings
NOMODIFY / NO_MODIFYsame NOMODIFY bitboth positive, two spellings

Permission handlers

Each FLAGBITENT carries a handler function that enforces who may set or clear the flag. The handlers, from least restrictive to most, are:

HandlerWho may set/clear
fh_anyAny player who controls the target.
fh_restrict_playerAnyone on non-players; wizards only on players. Used by FIXED and VACATION.
fh_player_bitAnyone, but never on player objects. Used by ROBOT.
fh_hear_bitLike fh_any, but also updates the hearing chain. Used by PUPPET, MONITOR, AUDIBLE.
fh_dark_bitSpecial rules for the DARK flag on players (requires Can_Hide or Can_Dark power, or wizard).
fh_staffStaff, Royalty, Wizards, or God.
fh_inheritOnly players with the INHERIT privilege.
fh_wizroyWizards or Royalty. Used by UNINSPECTED, NOEXAMINE, NOMODIFY, INDESTRUCTIBLE.
fh_wizWizards only. Used by BLIND, GAGGED, IMMORTAL, ROYALTY, STAFF, SLAVE, SUSPECT, OPEN_OK, SITEMON.
fh_privilegedGod on players; on non-players, a player who owns itself and already has the flag. Used for WIZARD/IMMORTAL propagation to objects.
fh_godGod (#1) only. Protects WIZARD, CONNECTED, internal markers.
fh_unicode / fh_asciiPlayers only; also updates the character-encoding state.

A special hard-coded rule prevents God from ever losing the WIZARD flag: fh_any checks for this case and refuses it with the message “You cannot make God mortal.”

Setting and clearing flags

The @set command calls flag_set() in flags.cpp. The function:

  1. Trims whitespace and checks for the ! negation prefix.
  2. Looks up the flag name in the hash map via find_flag().
  3. Handles negated aliases by flipping the sense.
  4. Checks whether the flag is already in the desired state.
  5. Checks NOMODIFY – non-WizRoy players cannot change flags on NOMODIFY objects.
  6. Calls the flag’s permission handler.
  7. Prints “Set.” or “Cleared.” (unless QUIET).

Multiple flags can be set in a single @set command, separated by spaces.

Accessor macros in C code

The codebase uses short macros to test flags. A sample:

#define Wizard(x)    (Flags(x) & WIZARD) || (Flags(Owner(x)) & WIZARD) && Inherits(x))
#define Dark(x)      ((Flags(x) & DARK) != 0) && (Can_Dark(x) || ...))
#define Halted(x)    ((Flags(x) & HALT) != 0)
#define Connected(x) ((Flags2(x) & CONNECTED) != 0) && isPlayer(x))
#define SiteMon(x)   ((Flags3(x) & SITEMON) != 0)

Wizard() and Royalty() cascade through Owner() and Inherits(), so an object owned by a wizard inherits wizard privileges if INHERIT is set. Setter macros like s_Halted(x) and s_Going(x) combine s_Flags() with the appropriate bitmask.

Softcode functions

FunctionBehavior
hasflag(<obj>, <flag>)Returns 1 if the object has the named flag, 0 otherwise. Also works on attribute flags when given <obj>/<attr> as the first argument.
orflags(<obj>, <letters>)Returns 1 if the object has any of the flags indicated by the single-character flag letters.
andflags(<obj>, <letters>)Returns 1 only if the object has all of the listed flag letters.
lflags(<obj>)Returns a space-separated list of the full flag names set on the object (or attribute).
flags(<obj>)Returns the compact single-character flag string, e.g. PWc for a connected player with the wizard flag.

The flags() output is produced by decode_flags(), which emits the object-type letter first (R, P, E, or space for things), followed by one character per set flag. A leading colon is inserted if the first flag letter would be a digit (to avoid ambiguity with dbref numbers).

The @flag command

@flag is a God-only command for renaming flags or removing aliases at runtime. It does not create new flag bits; it operates on the name table only.

@flag <existing>=<newname>     Rename a flag.
@flag/remove <alias>           Remove a flag alias.

The flag_access configuration directive in the .conf file can change which handler function governs a flag, allowing a game to tighten or loosen who may set specific flags without recompiling.

Marker flags

FLAG_WORD3 reserves ten bits (MARK_0 through MARK_9) as general-purpose marker flags. In the standard build these display as the digits 0 through 9 in the flag string. They are God-settable only by default, intended for use by global softcode systems that need to tag objects temporarily.

Database persistence

When the database is saved (flatfile or SQLite), all three flag words are written per object. On load, s_Flags() restores them. The @decompile command calls decompile_flags(), which iterates over gen_flag_names[] and emits @set commands for every set flag, skipping internal flags marked with CA_NO_DECOMP (such as CONNECTED, HAS_LISTEN, HAS_STARTUP, and UNICODE).