2007-01-02 22:44:14 +00:00
|
|
|
|
/*! \file flags.cpp
|
2007-01-03 03:11:53 +00:00
|
|
|
|
* \brief Flag manipulation routines.
|
2007-01-02 22:44:14 +00:00
|
|
|
|
*
|
|
|
|
|
|
*/
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
#include "copyright.h"
|
|
|
|
|
|
#include "autoconf.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include "externs.h"
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
#include "routing.h"
|
2022-03-18 23:33:03 -06:00
|
|
|
|
using namespace std;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_any: set or clear indicated bit, no security checking
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_any(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Never let God drop his/her own wizbit.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( God(target)
|
|
|
|
|
|
&& reset
|
|
|
|
|
|
&& flag == WIZARD
|
|
|
|
|
|
&& fflags == FLAG_WORD1)
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("You cannot make God mortal."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Otherwise we can go do it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (reset)
|
|
|
|
|
|
{
|
2026-03-04 23:16:08 -07:00
|
|
|
|
s_Flags(target, fflags, db[target].fs.word[fflags] & ~flag);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-04 23:16:08 -07:00
|
|
|
|
s_Flags(target, fflags, db[target].fs.word[fflags] | flag);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_room_bit: only settable on rooms (TYPE_ROOM).
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_room_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isRoom(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
bool result = fh_any(target, player, flag, fflags, reset);
|
|
|
|
|
|
if (result)
|
|
|
|
|
|
{
|
|
|
|
|
|
route_invalidate();
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_god: only GOD may set or clear the bit
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_god(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!God(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_wiz: only WIZARDS (or GOD) may set or clear the bit
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_wiz(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Wizard(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_wizroy: only WIZARDS, ROYALTY, (or GOD) may set or clear the bit
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_wizroy(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!WizRoy(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_restrict_player (renamed from fh_fixed): Only Wizards can set
|
|
|
|
|
|
* * this on players, but ordinary players can set it on other types
|
|
|
|
|
|
* * of objects.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static bool fh_restrict_player
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref target,
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
FLAG flag,
|
|
|
|
|
|
int fflags,
|
|
|
|
|
|
bool reset
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( isPlayer(target)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_privileged: You can set this flag on a non-player object, if you
|
|
|
|
|
|
* yourself have this flag and are a player who owns themselves (i.e.,
|
|
|
|
|
|
* no robots). Only God can set this on a player.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static bool fh_privileged
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref target,
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
FLAG flag,
|
|
|
|
|
|
int fflags,
|
|
|
|
|
|
bool reset
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!God(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( isPlayer(target)
|
|
|
|
|
|
|| !isPlayer(player)
|
|
|
|
|
|
|| player != Owner(player)
|
|
|
|
|
|
|| (db[player].fs.word[fflags] & flag) == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_inherit: only players may set or clear this bit.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_inherit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Inherits(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_dark_bit: manipulate the dark bit. Nonwizards may not set on players.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_dark_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !reset
|
|
|
|
|
|
&& isPlayer(target)
|
|
|
|
|
|
&& !( (target == player)
|
2026-03-04 15:00:05 -07:00
|
|
|
|
&& (Can_Hide(player) || Can_Dark(player)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_going_bit: manipulate the going bit. Non-gods may only clear.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_going_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Going(target)
|
|
|
|
|
|
&& reset
|
|
|
|
|
|
&& (Typeof(target) != TYPE_GARBAGE))
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("Your object has been spared from destruction."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!God(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Even God should not be allowed set protected dbrefs GOING.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !reset
|
|
|
|
|
|
&& ( target == 0
|
|
|
|
|
|
|| God(target)
|
|
|
|
|
|
|| target == mudconf.start_home
|
|
|
|
|
|
|| target == mudconf.start_room
|
|
|
|
|
|
|| target == mudconf.default_home
|
|
|
|
|
|
|| target == mudconf.master_room))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_hear_bit: set or clear bits that affect hearing.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_hear_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isPlayer(target) && (flag & MONITOR))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Can_Monitor(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool could_hear = Hearer(target);
|
|
|
|
|
|
bool result = fh_any(target, player, flag, fflags, reset);
|
|
|
|
|
|
handle_ears(target, could_hear, Hearer(target));
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_player_bit: Can set and reset this on everything but players.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static bool fh_player_bit
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref target,
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
FLAG flag,
|
|
|
|
|
|
int fflags,
|
|
|
|
|
|
bool reset
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isPlayer(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* fh_staff: only STAFF, WIZARDS, ROYALTY, (or GOD) may set or clear
|
|
|
|
|
|
* the bit.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static bool fh_staff
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref target,
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
FLAG flag,
|
|
|
|
|
|
int fflags,
|
|
|
|
|
|
bool reset
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Staff(player) && !God(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return (fh_any(target, player, flag, fflags, reset));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-10 01:03:25 +00:00
|
|
|
|
|
2007-03-09 10:05:04 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_unicode: only players may set or clear this bit.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static bool fh_unicode(dbref target, dbref player, FLAG flag, int fflags, bool reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isPlayer(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2007-03-10 06:27:22 +00:00
|
|
|
|
|
2007-03-10 06:40:15 +00:00
|
|
|
|
if (fh_any(target, player, flag, fflags, reset))
|
2007-03-09 10:05:04 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
if (!reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
set_player_encoding(target, CHARSET_UTF8);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2007-03-10 06:27:22 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
reset_player_encoding(target);
|
2007-03-10 06:27:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
return true;
|
2007-03-09 10:05:04 +00:00
|
|
|
|
}
|
2007-03-10 06:27:22 +00:00
|
|
|
|
return false;
|
2007-03-09 10:05:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-10 01:03:25 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * fh_ascii: only players may set or clear this bit.
|
|
|
|
|
|
*/
|
2007-03-10 02:49:14 +00:00
|
|
|
|
|
2022-03-15 19:47:08 -06:00
|
|
|
|
static bool fh_ascii(const dbref target, const dbref player, FLAG flag, int fflags, const bool reset)
|
2007-03-10 01:03:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (!isPlayer(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2022-03-15 19:47:08 -06:00
|
|
|
|
const bool result = fh_any(target, player, flag, fflags, reset);
|
2007-03-10 02:49:14 +00:00
|
|
|
|
|
|
|
|
|
|
if (result)
|
2007-03-10 01:03:25 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
if (!reset)
|
|
|
|
|
|
{
|
|
|
|
|
|
set_player_encoding(target, CHARSET_ASCII);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2007-03-10 01:03:25 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
reset_player_encoding(target);
|
2007-03-10 02:49:14 +00:00
|
|
|
|
}
|
2007-03-09 10:05:04 +00:00
|
|
|
|
}
|
2007-03-10 02:49:14 +00:00
|
|
|
|
|
2007-03-09 10:05:04 +00:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeAbode = { ABODE, 'A', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeAnsi = { ANSI, 'X', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeAudible = { HEARTHRU, 'a', FLAG_WORD1, 0, fh_hear_bit};
|
|
|
|
|
|
static FLAGBITENT fbeAuditorium = { AUDITORIUM, 'b', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeBlind = { BLIND, 'B', FLAG_WORD2, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeChownOk = { CHOWN_OK, 'C', FLAG_WORD1, 0, fh_any};
|
2010-08-09 07:58:40 -07:00
|
|
|
|
static FLAGBITENT fbeColor256 = { COLOR256, ' ', FLAG_WORD2, 0, fh_any};
|
2026-03-17 23:59:44 -06:00
|
|
|
|
static FLAGBITENT fbeTruecolor = { TRUECOLOR, ' ', FLAG_WORD2, 0, fh_any};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeConnected = { CONNECTED, 'c', FLAG_WORD2, CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeDark = { DARK, 'D', FLAG_WORD1, 0, fh_dark_bit};
|
|
|
|
|
|
static FLAGBITENT fbeDestroyOk = { DESTROY_OK, 'd', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeEnterOk = { ENTER_OK, 'e', FLAG_WORD1, 0, fh_any};
|
2026-03-03 18:39:35 -07:00
|
|
|
|
static FLAGBITENT fbeFixed = { FIXED_FLAG, 'f', FLAG_WORD2, 0, fh_restrict_player};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeFloating = { FLOATING, 'F', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeGagged = { GAGGED, 'j', FLAG_WORD2, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeGoing = { GOING, 'G', FLAG_WORD1, CA_NO_DECOMP, fh_going_bit};
|
|
|
|
|
|
static FLAGBITENT fbeHalted = { HALT, 'h', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeHasDaily = { HAS_DAILY, '*', FLAG_WORD2, CA_GOD|CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeHasForwardList = { HAS_FWDLIST, '&', FLAG_WORD2, CA_GOD|CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeHasListen = { HAS_LISTEN, '@', FLAG_WORD2, CA_GOD|CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeHasStartup = { HAS_STARTUP, '+', FLAG_WORD1, CA_GOD|CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeHaven = { HAVEN, 'H', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeHead = { HEAD_FLAG, '?', FLAG_WORD2, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeHtml = { HTML, '(', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeImmortal = { IMMORTAL, 'i', FLAG_WORD1, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeInherit = { INHERIT, 'I', FLAG_WORD1, 0, fh_inherit};
|
|
|
|
|
|
static FLAGBITENT fbeJumpOk = { JUMP_OK, 'J', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeKeepAlive = { CKEEPALIVE, 'k', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeKey = { KEY, 'K', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeLight = { LIGHT, 'l', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeLinkOk = { LINK_OK, 'L', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeMonitor = { MONITOR, 'M', FLAG_WORD1, 0, fh_hear_bit};
|
|
|
|
|
|
static FLAGBITENT fbeMyopic = { MYOPIC, 'm', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeNoCommand = { NO_COMMAND, 'n', FLAG_WORD2, 0, fh_any};
|
2007-03-10 04:29:47 +00:00
|
|
|
|
static FLAGBITENT fbeAscii = { ASCII , '~', FLAG_WORD2, 0, fh_ascii};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeNoBleed = { NOBLEED, '-', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeNoSpoof = { NOSPOOF, 'N', FLAG_WORD1, 0, fh_any};
|
2007-04-04 01:32:01 +00:00
|
|
|
|
static FLAGBITENT fbeOpaque = { MUX_OPAQUE, 'O', FLAG_WORD1, 0, fh_any};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeOpenOk = { OPEN_OK, 'z', FLAG_WORD2, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeParentOk = { PARENT_OK, 'Y', FLAG_WORD2, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbePlayerMails = { PLAYER_MAILS, ' ', FLAG_WORD2, CA_GOD|CA_NO_DECOMP, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbePuppet = { PUPPET, 'p', FLAG_WORD1, 0, fh_hear_bit};
|
|
|
|
|
|
static FLAGBITENT fbeQuiet = { QUIET, 'Q', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeRobot = { ROBOT, 'r', FLAG_WORD1, 0, fh_player_bit};
|
|
|
|
|
|
static FLAGBITENT fbeRoyalty = { ROYALTY, 'Z', FLAG_WORD1, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeSafe = { SAFE, 's', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeSlave = { SLAVE, 'x', FLAG_WORD2, CA_WIZARD, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeStaff = { STAFF, 'w', FLAG_WORD2, 0, fh_wiz};
|
|
|
|
|
|
static FLAGBITENT fbeSticky = { STICKY, 'S', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeSuspect = { SUSPECT, 'u', FLAG_WORD2, CA_WIZARD, fh_wiz};
|
2026-03-03 21:54:49 -07:00
|
|
|
|
static FLAGBITENT fbeTalkMode = { TALKMODE, ' ', FLAG_WORD2, 0, fh_any};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeTerse = { TERSE, 'q', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeTrace = { TRACE, 'T', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeTransparent = { SEETHRU, 't', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeUnfindable = { UNFINDABLE, 'U', FLAG_WORD2, 0, fh_any};
|
2007-04-04 01:32:01 +00:00
|
|
|
|
static FLAGBITENT fbeUnicode = { MUX_UNICODE, ' ', FLAG_WORD3, CA_NO_DECOMP, fh_unicode};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeUninspected = { UNINSPECTED, 'g', FLAG_WORD2, 0, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeVacation = { VACATION, '|', FLAG_WORD2, 0, fh_restrict_player};
|
|
|
|
|
|
static FLAGBITENT fbeVerbose = { VERBOSE, 'v', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeVisual = { VISUAL, 'V', FLAG_WORD1, 0, fh_any};
|
|
|
|
|
|
static FLAGBITENT fbeWizard = { WIZARD, 'W', FLAG_WORD1, 0, fh_god};
|
2026-03-03 22:23:55 -07:00
|
|
|
|
static FLAGBITENT fbeAlone = { ALONE, ' ', FLAG_WORD3, 0, fh_wiz};
|
2026-03-04 11:06:31 -07:00
|
|
|
|
static FLAGBITENT fbeNoExamine = { NOEXAMINE, 'E', FLAG_WORD3, 0, fh_wizroy};
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
static FLAGBITENT fbeNoModify = { NOMODIFY, ' ', FLAG_WORD3, 0, fh_wizroy};
|
2026-03-04 11:29:40 -07:00
|
|
|
|
static FLAGBITENT fbeIndestructible = { INDESTRUCTIBLE, ' ', FLAG_WORD3, 0, fh_wizroy};
|
Implement NOEVAL object flag and attribute flag (#11).
Add NOEVAL object flag (FLAG_WORD3) and AF_NOEVAL attribute flag so that
attribute contents can be stored as literal data without evaluation.
When either flag is set, u(), ulocal(), get_eval(), @trigger, did_it()
messages/actions, @function, modSpeech, check_filter, make_prefix,
process_hook, sortby comparison, eval_boolexp, and format attributes
(EXITFORMAT/CONFORMAT/NAMEFORMAT/DESCFORMAT) all return raw text with
no function calls, %-substitutions, or escape processing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:04:14 -07:00
|
|
|
|
static FLAGBITENT fbeNoEval = { NOEVAL, ' ', FLAG_WORD3, 0, fh_any};
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
static FLAGBITENT fbeNavigable = { NAVIGABLE, ' ', FLAG_WORD3, 0, fh_room_bit};
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static FLAGBITENT fbeSitemon = { SITEMON, '$', FLAG_WORD3, 0, fh_wiz};
|
|
|
|
|
|
#ifdef WOD_REALMS
|
|
|
|
|
|
static FLAGBITENT fbeFae = { FAE, '0', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeChimera = { CHIMERA, '1', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbePeering = { PEERING, '2', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeUmbra = { UMBRA, '3', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeShroud = { SHROUD, '4', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeMatrix = { MATRIX, '5', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeObf = { OBF, '6', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeHss = { HSS, '7', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeMedium = { MEDIUM, '8', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeDead = { DEAD, '9', FLAG_WORD3, CA_STAFF, fh_wizroy};
|
|
|
|
|
|
static FLAGBITENT fbeMarker0 = { MARK_0, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker1 = { MARK_1, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker2 = { MARK_2, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker3 = { MARK_3, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker4 = { MARK_4, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker5 = { MARK_5, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker6 = { MARK_6, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker7 = { MARK_7, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker8 = { MARK_8, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker9 = { MARK_9, ' ', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
#else // WOD_REALMS
|
|
|
|
|
|
static FLAGBITENT fbeMarker0 = { MARK_0, '0', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker1 = { MARK_1, '1', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker2 = { MARK_2, '2', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker3 = { MARK_3, '3', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker4 = { MARK_4, '4', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker5 = { MARK_5, '5', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker6 = { MARK_6, '6', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker7 = { MARK_7, '7', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker8 = { MARK_8, '8', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
static FLAGBITENT fbeMarker9 = { MARK_9, '9', FLAG_WORD3, 0, fh_god};
|
|
|
|
|
|
#endif // WOD_REALMS
|
|
|
|
|
|
|
|
|
|
|
|
FLAGNAMEENT gen_flag_names[] =
|
|
|
|
|
|
{
|
2026-03-03 22:23:55 -07:00
|
|
|
|
{T("ALONE"), true, &fbeAlone },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("ABODE"), true, &fbeAbode },
|
|
|
|
|
|
{T("ACCENTS"), false, &fbeAscii },
|
|
|
|
|
|
{T("ANSI"), true, &fbeAnsi },
|
|
|
|
|
|
{T("ASCII"), true, &fbeAscii },
|
|
|
|
|
|
{T("AUDIBLE"), true, &fbeAudible },
|
|
|
|
|
|
{T("AUDITORIUM"), true, &fbeAuditorium },
|
|
|
|
|
|
{T("BLEED"), false, &fbeNoBleed },
|
|
|
|
|
|
{T("BLIND"), true, &fbeBlind },
|
|
|
|
|
|
{T("COMMANDS"), false, &fbeNoCommand },
|
|
|
|
|
|
{T("CHOWN_OK"), true, &fbeChownOk },
|
|
|
|
|
|
{T("COLOR256"), true, &fbeColor256 },
|
|
|
|
|
|
{T("CONNECTED"), true, &fbeConnected },
|
|
|
|
|
|
{T("DARK"), true, &fbeDark },
|
|
|
|
|
|
{T("DESTROY_OK"), true, &fbeDestroyOk },
|
|
|
|
|
|
{T("ENTER_OK"), true, &fbeEnterOk },
|
|
|
|
|
|
{T("FIXED"), true, &fbeFixed },
|
|
|
|
|
|
{T("FLOATING"), true, &fbeFloating },
|
|
|
|
|
|
{T("GAGGED"), true, &fbeGagged },
|
|
|
|
|
|
{T("GOING"), true, &fbeGoing },
|
|
|
|
|
|
{T("HALTED"), true, &fbeHalted },
|
|
|
|
|
|
{T("HAS_DAILY"), true, &fbeHasDaily },
|
|
|
|
|
|
{T("HAS_FORWARDLIST"), true, &fbeHasForwardList },
|
|
|
|
|
|
{T("HAS_LISTEN"), true, &fbeHasListen },
|
|
|
|
|
|
{T("HAS_STARTUP"), true, &fbeHasStartup },
|
|
|
|
|
|
{T("HAVEN"), true, &fbeHaven },
|
|
|
|
|
|
{T("HEAD"), true, &fbeHead },
|
|
|
|
|
|
{T("HTML"), true, &fbeHtml },
|
|
|
|
|
|
{T("IMMORTAL"), true, &fbeImmortal },
|
2026-03-04 11:29:40 -07:00
|
|
|
|
{T("INDESTRUCTIBLE"), true, &fbeIndestructible },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("INHERIT"), true, &fbeInherit },
|
|
|
|
|
|
{T("JUMP_OK"), true, &fbeJumpOk },
|
|
|
|
|
|
{T("KEEPALIVE"), true, &fbeKeepAlive },
|
|
|
|
|
|
{T("KEY"), true, &fbeKey },
|
|
|
|
|
|
{T("LIGHT"), true, &fbeLight },
|
|
|
|
|
|
{T("LINK_OK"), true, &fbeLinkOk },
|
|
|
|
|
|
{T("MARKER0"), true, &fbeMarker0 },
|
|
|
|
|
|
{T("MARKER1"), true, &fbeMarker1 },
|
|
|
|
|
|
{T("MARKER2"), true, &fbeMarker2 },
|
|
|
|
|
|
{T("MARKER3"), true, &fbeMarker3 },
|
|
|
|
|
|
{T("MARKER4"), true, &fbeMarker4 },
|
|
|
|
|
|
{T("MARKER5"), true, &fbeMarker5 },
|
|
|
|
|
|
{T("MARKER6"), true, &fbeMarker6 },
|
|
|
|
|
|
{T("MARKER7"), true, &fbeMarker7 },
|
|
|
|
|
|
{T("MARKER8"), true, &fbeMarker8 },
|
|
|
|
|
|
{T("MARKER9"), true, &fbeMarker9 },
|
|
|
|
|
|
{T("MONITOR"), true, &fbeMonitor },
|
|
|
|
|
|
{T("MYOPIC"), true, &fbeMyopic },
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
{T("NAVIGABLE"), true, &fbeNavigable },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("NO_COMMAND"), true, &fbeNoCommand },
|
2026-03-04 11:06:31 -07:00
|
|
|
|
{T("NO_EXAMINE"), true, &fbeNoExamine },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("NOBLEED"), true, &fbeNoBleed },
|
Implement NOEVAL object flag and attribute flag (#11).
Add NOEVAL object flag (FLAG_WORD3) and AF_NOEVAL attribute flag so that
attribute contents can be stored as literal data without evaluation.
When either flag is set, u(), ulocal(), get_eval(), @trigger, did_it()
messages/actions, @function, modSpeech, check_filter, make_prefix,
process_hook, sortby comparison, eval_boolexp, and format attributes
(EXITFORMAT/CONFORMAT/NAMEFORMAT/DESCFORMAT) all return raw text with
no function calls, %-substitutions, or escape processing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:04:14 -07:00
|
|
|
|
{T("NOEVAL"), true, &fbeNoEval },
|
2026-03-04 11:06:31 -07:00
|
|
|
|
{T("NOEXAMINE"), true, &fbeNoExamine },
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
{T("NOMODIFY"), true, &fbeNoModify },
|
|
|
|
|
|
{T("NO_MODIFY"), true, &fbeNoModify },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("NOSPOOF"), true, &fbeNoSpoof },
|
|
|
|
|
|
{T("OPAQUE"), true, &fbeOpaque },
|
|
|
|
|
|
{T("OPEN_OK"), true, &fbeOpenOk },
|
|
|
|
|
|
{T("PARENT_OK"), true, &fbeParentOk },
|
|
|
|
|
|
{T("PLAYER_MAILS"), true, &fbePlayerMails },
|
|
|
|
|
|
{T("PUPPET"), true, &fbePuppet },
|
|
|
|
|
|
{T("QUIET"), true, &fbeQuiet },
|
|
|
|
|
|
{T("ROBOT"), true, &fbeRobot },
|
|
|
|
|
|
{T("ROYALTY"), true, &fbeRoyalty },
|
|
|
|
|
|
{T("SAFE"), true, &fbeSafe },
|
|
|
|
|
|
{T("SITEMON"), true, &fbeSitemon },
|
|
|
|
|
|
{T("SLAVE"), true, &fbeSlave },
|
|
|
|
|
|
{T("SPOOF"), false, &fbeNoSpoof },
|
|
|
|
|
|
{T("STAFF"), true, &fbeStaff },
|
|
|
|
|
|
{T("STICKY"), true, &fbeSticky },
|
|
|
|
|
|
{T("SUSPECT"), true, &fbeSuspect },
|
2026-03-03 21:54:49 -07:00
|
|
|
|
{T("TALKMODE"), true, &fbeTalkMode },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("TERSE"), true, &fbeTerse },
|
|
|
|
|
|
{T("TRACE"), true, &fbeTrace },
|
|
|
|
|
|
{T("TRANSPARENT"), true, &fbeTransparent },
|
2026-03-17 23:59:44 -06:00
|
|
|
|
{T("TRUECOLOR"), true, &fbeTruecolor },
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("UNFINDABLE"), true, &fbeUnfindable },
|
|
|
|
|
|
{T("UNICODE"), true, &fbeUnicode },
|
|
|
|
|
|
{T("UNINSPECTED"), true, &fbeUninspected },
|
|
|
|
|
|
{T("VACATION"), true, &fbeVacation },
|
|
|
|
|
|
{T("VERBOSE"), true, &fbeVerbose },
|
|
|
|
|
|
{T("VISUAL"), true, &fbeVisual },
|
|
|
|
|
|
{T("WIZARD"), true, &fbeWizard },
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#ifdef WOD_REALMS
|
2026-03-03 18:39:35 -07:00
|
|
|
|
{T("FAE"), true, &fbeFae },
|
|
|
|
|
|
{T("CHIMERA"), true, &fbeChimera },
|
|
|
|
|
|
{T("PEERING"), true, &fbePeering },
|
|
|
|
|
|
{T("UMBRA"), true, &fbeUmbra },
|
|
|
|
|
|
{T("SHROUD"), true, &fbeShroud },
|
|
|
|
|
|
{T("MATRIX"), true, &fbeMatrix },
|
|
|
|
|
|
{T("OBF"), true, &fbeObf },
|
|
|
|
|
|
{T("HSS"), true, &fbeHss },
|
|
|
|
|
|
{T("MEDIUM"), true, &fbeMedium },
|
|
|
|
|
|
{T("DEAD"), true, &fbeDead },
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#endif // WOD_REALMS
|
2026-03-03 19:38:14 -07:00
|
|
|
|
{nullptr, false, nullptr}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
OBJENT object_types[8] =
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
{T("ROOM"), 'R', CA_PUBLIC, OF_CONTENTS|OF_EXITS|OF_DROPTO|OF_HOME},
|
|
|
|
|
|
{T("THING"), ' ', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_SIBLINGS},
|
|
|
|
|
|
{T("EXIT"), 'E', CA_PUBLIC, OF_SIBLINGS},
|
|
|
|
|
|
{T("PLAYER"), 'P', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_OWNER|OF_SIBLINGS},
|
|
|
|
|
|
{T("TYPE5"), '+', CA_GOD, 0},
|
|
|
|
|
|
{T("GARBAGE"), '-', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_SIBLINGS},
|
|
|
|
|
|
{T("GARBAGE"), '#', CA_GOD, 0},
|
|
|
|
|
|
{T("GARBAGE"), '=', CA_GOD, 0}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* init_flagtab: initialize flag hash tables.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void init_flagtab(void)
|
|
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
for (FLAGNAMEENT *flag_name_entity = gen_flag_names; flag_name_entity->pOrigName; flag_name_entity++)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
flag_name_entity->flagname = const_cast<UTF8*>(flag_name_entity->pOrigName);
|
|
|
|
|
|
const size_t flag_name_length = strlen(reinterpret_cast<const char*>(flag_name_entity->pOrigName));
|
|
|
|
|
|
vector<UTF8> flag_name(flag_name_entity->pOrigName, flag_name_entity->pOrigName + flag_name_length);
|
|
|
|
|
|
auto it = mudstate.flag_names_map.find(flag_name);
|
|
|
|
|
|
if (it == mudstate.flag_names_map.end())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
mudstate.flag_names_map.insert(make_pair(flag_name, flag_name_entity));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* display_flags: display available flags.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void display_flagtab(dbref player)
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
|
|
|
|
|
|
Migrate 22 alloc_lbuf/free_lbuf sites to LBuf RAII
Replace manual alloc_lbuf/free_lbuf pairs with LBuf RAII wrappers
in 12 engine source files: rob, walk, quota, wiz, session, object,
log, match, move, create, flags, boolexp. This eliminates ~40
explicit free_lbuf calls on error paths that are now handled by
destructors, removing leak risk in early-return and multi-exit
functions (boolexp alone had 13 exit-path frees across two
functions). Buffers returned by atr_get/atr_pget or to callers
are left manual since LBuf cannot adopt externally-allocated
buffers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:46:22 -06:00
|
|
|
|
LBuf buf = LBuf_Src("display_flagtab");
|
|
|
|
|
|
UTF8 *bp = buf.get();
|
nls: mark object/flags/boolexp create-and-flag notify prose with M_()
Phase 3 coverage slice. Player and staff notifies that still used T()
(cast-only) now use M_() so they extract into the catalogue:
object name taken/silly, deposit refund, @chown summary, parent/zone/
home/dropto clears, floating room
flags Flags: header, type/flag parse errors, @set decompile, flag
name removed
boolexp match “don’t see / which”
wiz password changed by %s
player @protect all listing line
Left alone: log messages, HTML, softcode/machine tokens, concatenated
fragments (set @chown owner line, rob give pieces), and punctuation
separators. pot 704 -> 722; xx filled; ko msgmerge only.
2026-07-28 13:06:50 +00:00
|
|
|
|
safe_str(M_("Flags:"), buf, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for (fp = gen_flag_names; fp->flagname; fp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
if ( ( (fbe->listperm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_GOD)
|
|
|
|
|
|
&& !God(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
safe_chr(' ', buf, &bp);
|
|
|
|
|
|
safe_str(fp->flagname, buf, &bp);
|
|
|
|
|
|
if (fbe->flaglett != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_chr('(', buf, &bp);
|
|
|
|
|
|
if (!fp->bPositive)
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_chr('!', buf, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
safe_chr(fbe->flaglett, buf, &bp);
|
|
|
|
|
|
safe_chr(')', buf, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify(player, buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *MakeCanonicalFlagName
|
2006-09-01 22:59:23 +00:00
|
|
|
|
(
|
2007-03-14 00:55:08 +00:00
|
|
|
|
const UTF8 *pName,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int *pnName,
|
|
|
|
|
|
bool *pbValid
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
Convert all static scratch buffers to thread_local
43 static scratch-buffer arrays across 21 files (5 in mux/src, 38 in
mux/modules/) changed from `static` to `thread_local`. Under the
current single-threaded evaluator this is a zero-behavior-change swap
— `thread_local` storage has the same lifetime and zero-allocation
properties as `static` — but each thread gets its own copy, which
makes these functions safe for a future multi-threaded evaluator
without any locking.
Read-only constant tables (`aRadix64`, `aRadixPenn36`,
`aRadixPenn64`, `Empty`) left as `static` because they are immutable
shared data.
Affected areas:
net.cpp — queue_string co_buf, trimmed_site, dump_users NameField
signals.cpp — signal_desc
stubslave.cpp — Stub_PipePump
attrcache.cpp — sqlite_attr_buf
boolexp.cpp — parsestore
command.cpp — preserve_cmd, SpaceCompressCommand, LowerCaseCommand
comsys.cpp — NewTitle, Buffer, temp
db.cpp — tbuff, Buffer (x2)
flags.cpp — buff
funceval.cpp — textbuff
functions.cpp — TimeBuffer64, TimeBuffer80, Buffer
help.cpp — Line, Buffer
mail.cpp — aFolders, Buffer, res, szFittedMailAliasDesc
match.cpp — buffer
player.cpp — szSalt, buf (x2), buff
plusemail.cpp — buf
predicates.cpp — Buf (x2), pName
session.cpp — szFittedDoing
set.cpp — pRestrictedKeyText
unparse.cpp — buf, boolexp_buf
mail_mod.cpp — result, res, buf
All 21 files verified with g++ -std=c++17 -fsyntax-only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:21:14 -06:00
|
|
|
|
thread_local UTF8 buff[SBUF_SIZE];
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *p = buff;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int nName = 0;
|
|
|
|
|
|
|
Unicode case-folding at all call sites (#415, #416, #417, #418, #420, #421, #422, #423).
Replace mux_toupper_ascii[]/mux_tolower_ascii[] with Unicode-aware
state-machine transforms (mux_toupper()/mux_tolower()) at 12 call
sites across 9 files:
- Add mux_toupper_first() helper for first-code-point uppercasing
- Rewrite mux_stricmp, mux_memicmp, string_compare, string_prefix,
minmatch, matches_exit_from_list for code-point-at-a-time comparison
- Rewrite BMH*I functions to pre-lowercase and delegate to
case-sensitive versions
- Convert string canonicalization sites in eval.cpp, flags.cpp,
functions.cpp, command.cpp to use mux_strupr()/mux_strlwr()
- Pre-lowercase wildcard patterns in wild.cpp entry points
- Add safe-ASCII comments at sites with guaranteed-ASCII data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:20:53 -07:00
|
|
|
|
// Uppercase flag name one code point at a time (Unicode-aware).
|
|
|
|
|
|
//
|
|
|
|
|
|
while ('\0' != *pName)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Unicode case-folding at all call sites (#415, #416, #417, #418, #420, #421, #422, #423).
Replace mux_toupper_ascii[]/mux_tolower_ascii[] with Unicode-aware
state-machine transforms (mux_toupper()/mux_tolower()) at 12 call
sites across 9 files:
- Add mux_toupper_first() helper for first-code-point uppercasing
- Rewrite mux_stricmp, mux_memicmp, string_compare, string_prefix,
minmatch, matches_exit_from_list for code-point-at-a-time comparison
- Rewrite BMH*I functions to pre-lowercase and delegate to
case-sensitive versions
- Convert string canonicalization sites in eval.cpp, flags.cpp,
functions.cpp, command.cpp to use mux_strupr()/mux_strlwr()
- Pre-lowercase wildcard patterns in wild.cpp entry points
- Add safe-ASCII comments at sites with guaranteed-ASCII data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:20:53 -07:00
|
|
|
|
size_t n = utf8_FirstByte[static_cast<unsigned char>(*pName)];
|
|
|
|
|
|
if (n >= UTF8_CONTINUE)
|
|
|
|
|
|
{
|
2026-03-05 21:27:56 -07:00
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t j = 1; j < n; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( '\0' == pName[j]
|
|
|
|
|
|
|| UTF8_CONTINUE != utf8_FirstByte[static_cast<unsigned char>(pName[j])])
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
Unicode case-folding at all call sites (#415, #416, #417, #418, #420, #421, #422, #423).
Replace mux_toupper_ascii[]/mux_tolower_ascii[] with Unicode-aware
state-machine transforms (mux_toupper()/mux_tolower()) at 12 call
sites across 9 files:
- Add mux_toupper_first() helper for first-code-point uppercasing
- Rewrite mux_stricmp, mux_memicmp, string_compare, string_prefix,
minmatch, matches_exit_from_list for code-point-at-a-time comparison
- Rewrite BMH*I functions to pre-lowercase and delegate to
case-sensitive versions
- Convert string canonicalization sites in eval.cpp, flags.cpp,
functions.cpp, command.cpp to use mux_strupr()/mux_strlwr()
- Pre-lowercase wildcard patterns in wild.cpp entry points
- Add safe-ASCII comments at sites with guaranteed-ASCII data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:20:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bXor;
|
|
|
|
|
|
const string_desc *qDesc = mux_toupper(pName, bXor);
|
|
|
|
|
|
size_t m;
|
|
|
|
|
|
if (nullptr == qDesc)
|
|
|
|
|
|
{
|
|
|
|
|
|
m = n;
|
|
|
|
|
|
if (nName + static_cast<int>(m) >= SBUF_SIZE)
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (size_t j = 0; j < m; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
*p++ = pName[j];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
m = qDesc->n_bytes;
|
2026-03-05 21:27:56 -07:00
|
|
|
|
if ( bXor
|
|
|
|
|
|
&& m != n)
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
Unicode case-folding at all call sites (#415, #416, #417, #418, #420, #421, #422, #423).
Replace mux_toupper_ascii[]/mux_tolower_ascii[] with Unicode-aware
state-machine transforms (mux_toupper()/mux_tolower()) at 12 call
sites across 9 files:
- Add mux_toupper_first() helper for first-code-point uppercasing
- Rewrite mux_stricmp, mux_memicmp, string_compare, string_prefix,
minmatch, matches_exit_from_list for code-point-at-a-time comparison
- Rewrite BMH*I functions to pre-lowercase and delegate to
case-sensitive versions
- Convert string canonicalization sites in eval.cpp, flags.cpp,
functions.cpp, command.cpp to use mux_strupr()/mux_strlwr()
- Pre-lowercase wildcard patterns in wild.cpp entry points
- Add safe-ASCII comments at sites with guaranteed-ASCII data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:20:53 -07:00
|
|
|
|
if (nName + static_cast<int>(m) >= SBUF_SIZE)
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (bXor)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (size_t j = 0; j < m; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
*p++ = pName[j] ^ qDesc->p[j];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (size_t j = 0; j < m; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
*p++ = qDesc->p[j];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
nName += static_cast<int>(m);
|
2026-03-05 21:27:56 -07:00
|
|
|
|
pName += n;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
*p = '\0';
|
2006-09-29 07:09:09 +00:00
|
|
|
|
if ( 0 < nName
|
|
|
|
|
|
&& nName < SBUF_SIZE)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
*pnName = nName;
|
|
|
|
|
|
*pbValid = true;
|
|
|
|
|
|
return buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-18 23:33:03 -06:00
|
|
|
|
static FLAGNAMEENT* find_flag(const UTF8* input_flag_name)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
// Convert input_flag_name to canonical lowercase format.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2022-03-18 23:33:03 -06:00
|
|
|
|
int flag_name_length;
|
|
|
|
|
|
bool valid;
|
|
|
|
|
|
UTF8* flag_name = MakeCanonicalFlagName(input_flag_name, &flag_name_length, &valid);
|
|
|
|
|
|
if (valid)
|
|
|
|
|
|
{
|
|
|
|
|
|
const vector<UTF8> name(flag_name, flag_name + flag_name_length);
|
|
|
|
|
|
const auto it = mudstate.flag_names_map.find(name);
|
|
|
|
|
|
if (it != mudstate.flag_names_map.end())
|
|
|
|
|
|
{
|
|
|
|
|
|
return it->second;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2022-03-18 23:33:03 -06:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// flag_set: Set or clear a specified flag on an object.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
void flag_set(dbref target, dbref player, UTF8 *flag, int key)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
bool bDone = false;
|
|
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
// Trim spaces, and handle the negation character.
|
|
|
|
|
|
//
|
|
|
|
|
|
while (mux_isspace(*flag))
|
|
|
|
|
|
{
|
|
|
|
|
|
flag++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bNegate = false;
|
|
|
|
|
|
if (*flag == '!')
|
|
|
|
|
|
{
|
|
|
|
|
|
bNegate = true;
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
flag++;
|
|
|
|
|
|
} while (mux_isspace(*flag));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Beginning of flag name is now 'flag'.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *nflag = flag;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
while ( *nflag != '\0'
|
|
|
|
|
|
&& !mux_isspace(*nflag))
|
|
|
|
|
|
{
|
|
|
|
|
|
nflag++;
|
|
|
|
|
|
}
|
2006-09-06 22:12:43 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (*nflag == '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
bDone = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*nflag = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure a flag name was specified.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (*flag == '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bNegate)
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("You must specify a flag to clear."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("You must specify a flag to set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGNAMEENT *fp = find_flag(flag);
|
|
|
|
|
|
if (!fp)
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("I do not understand that flag."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
|
|
|
|
|
|
bool bClearSet = bNegate;
|
|
|
|
|
|
if (!fp->bPositive)
|
|
|
|
|
|
{
|
|
|
|
|
|
bNegate = !bNegate;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:21:22 -07:00
|
|
|
|
// Check if the flag is already in the desired state.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2026-03-03 20:21:22 -07:00
|
|
|
|
bool bCurrentlySet = (db[target].fs.word[fbe->flagflag] & fbe->flagvalue) != 0;
|
|
|
|
|
|
if (bNegate == bCurrentlySet)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
// State needs to change. Check NOMODIFY first.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (NoModify(target) && !WizRoy(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
bDone = true;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invoke the flag handler, and print feedback.
|
2026-03-03 20:21:22 -07:00
|
|
|
|
//
|
|
|
|
|
|
if (!fbe->handler(target, player, fbe->flagvalue, fbe->flagflag, bNegate))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!(key & SET_QUIET) && !Quiet(player))
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, (bClearSet ? M_("Cleared.") : M_("Set.")));
|
2026-03-03 20:21:22 -07:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (!(key & SET_QUIET) && !Quiet(player))
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, (bClearSet ? M_("Already cleared.") : M_("Already set.")));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
flag = nflag + 1;
|
|
|
|
|
|
|
|
|
|
|
|
} while (!bDone);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * decode_flags: converts a flags word into corresponding letters.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *decode_flags(dbref player, FLAGSET *fs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *buf, *bp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
buf = bp = alloc_sbuf("decode_flags");
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
if (!Good_obj(player))
|
|
|
|
|
|
{
|
2026-07-27 00:47:44 +00:00
|
|
|
|
mux_strncpy(buf, S_("#-2 ERROR"), SBUF_SIZE-1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
int flagtype = fs->word[FLAG_WORD1] & TYPE_MASK;
|
|
|
|
|
|
bool bNeedColon = true;
|
|
|
|
|
|
if (object_types[flagtype].lett != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_sb_chr(object_types[flagtype].lett, buf, &bp);
|
|
|
|
|
|
bNeedColon = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
|
|
|
|
|
for (fp = gen_flag_names; fp->flagname; fp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
if ( !fp->bPositive
|
|
|
|
|
|
|| fbe->flaglett == ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
// Only look at positive-sense entries that have non-space flag
|
|
|
|
|
|
// letters.
|
|
|
|
|
|
//
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (fs->word[fbe->flagflag] & fbe->flagvalue)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ( (fbe->listperm & CA_STAFF)
|
|
|
|
|
|
&& !Staff(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_ADMIN)
|
|
|
|
|
|
&& !WizRoy(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_GOD)
|
|
|
|
|
|
&& !God(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
// Don't show CONNECT on hidden players to those who can't see
|
|
|
|
|
|
// them. Test DARK alone -- that is what Hidden() means -- to
|
|
|
|
|
|
// match has_flag() and flag_description(). Requiring WIZARD too
|
|
|
|
|
|
// leaked the 'c' letter for every dark non-wizard: royalty,
|
|
|
|
|
|
// staff, and any mortal able to set itself DARK (#1184). This
|
|
|
|
|
|
// takes a FLAGSET rather than a dbref, so the test is on the
|
|
|
|
|
|
// flagset the caller passed, which is the target's.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
|
|
|
|
|
if ( flagtype == TYPE_PLAYER
|
|
|
|
|
|
&& fbe->flagflag == FLAG_WORD2
|
|
|
|
|
|
&& fbe->flagvalue == CONNECTED
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
&& (fs->word[FLAG_WORD1] & DARK) != 0
|
2006-09-01 22:59:23 +00:00
|
|
|
|
&& !See_Hidden(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( bNeedColon
|
|
|
|
|
|
&& mux_isdigit(fbe->flaglett))
|
|
|
|
|
|
{
|
|
|
|
|
|
// We can't allow numerical digits at the beginning.
|
|
|
|
|
|
//
|
|
|
|
|
|
safe_sb_chr(':', buf, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
safe_sb_chr(fbe->flaglett, buf, &bp);
|
|
|
|
|
|
bNeedColon = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * has_flag: does object have flag visible to player?
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 22:33:04 +00:00
|
|
|
|
bool has_flag(dbref player, dbref it, const UTF8 *flagname)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
FLAGNAMEENT *fp = find_flag(flagname);
|
|
|
|
|
|
if (!fp)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( fp->bPositive
|
|
|
|
|
|
&& (db[it].fs.word[fbe->flagflag] & fbe->flagvalue))
|
|
|
|
|
|
|| ( !fp->bPositive
|
|
|
|
|
|
&& (db[it].fs.word[fbe->flagflag] & fbe->flagvalue) == 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ( (fbe->listperm & CA_STAFF)
|
|
|
|
|
|
&& !Staff(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_ADMIN)
|
|
|
|
|
|
&& !WizRoy(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_GOD)
|
|
|
|
|
|
&& !God(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Don't show CONNECT on dark wizards to mortals
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( isPlayer(it)
|
|
|
|
|
|
&& (fbe->flagvalue == CONNECTED)
|
|
|
|
|
|
&& (fbe->flagflag == FLAG_WORD2)
|
|
|
|
|
|
&& Hidden(it)
|
|
|
|
|
|
&& !See_Hidden(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * flag_description: Return an mbuf containing the type and flags on thing.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *flag_description(dbref player, dbref target)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Allocate the return buffer.
|
|
|
|
|
|
//
|
|
|
|
|
|
int otype = Typeof(target);
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *buff = alloc_mbuf("flag_description");
|
|
|
|
|
|
UTF8 *bp = buff;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Store the header strings and object type.
|
|
|
|
|
|
//
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_mb_str(T("Type: "), buff, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_mb_str(object_types[otype].name, buff, &bp);
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_mb_str(T(" Flags:"), buff, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (object_types[otype].perm != CA_PUBLIC)
|
|
|
|
|
|
{
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
return buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Store the type-invariant flags.
|
|
|
|
|
|
//
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
|
|
|
|
|
for (fp = gen_flag_names; fp->flagname; fp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!fp->bPositive)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
if (db[target].fs.word[fbe->flagflag] & fbe->flagvalue)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ( (fbe->listperm & CA_STAFF)
|
|
|
|
|
|
&& !Staff(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_ADMIN)
|
|
|
|
|
|
&& !WizRoy(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_GOD)
|
|
|
|
|
|
&& !God(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Don't show CONNECT on dark wizards to mortals.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( isPlayer(target)
|
|
|
|
|
|
&& (fbe->flagvalue == CONNECTED)
|
|
|
|
|
|
&& (fbe->flagflag == FLAG_WORD2)
|
|
|
|
|
|
&& Hidden(target)
|
|
|
|
|
|
&& !See_Hidden(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
safe_mb_chr(' ', buff, &bp);
|
|
|
|
|
|
safe_mb_str(fp->flagname, buff, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Terminate the string, and return the buffer to the caller.
|
|
|
|
|
|
//
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
return buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * Return an lbuf containing the name and number of an object
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *unparse_object_numonly(dbref target)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
engine: add LBufPtr owning handle; convert a verified subset to RAII (#717)
Incremental progress on the alloc_lbuf/free_lbuf -> LBuf RAII migration, adding
the structural enabler the issue calls for and converting a small, individually
verified set of the harder (ownership-escaping) sites.
alloc.h:
- LBuf gains release() (relinquish ownership to a caller) and reset() (free
now, become empty). release() lets a producer that returns an lbuf use RAII
for its early/error paths and hand the buffer off at the success return.
- New LBufPtr: an owning, nullable, movable handle. Unlike LBuf (which always
allocates and suits scoped scratch), LBufPtr can be empty, reseated, and
released — for conditional ownership, storing an lbuf in a struct/queue
entry, or producing a buffer to return. LBufPtr_Src / LBufPtr_Adopt mirror
the LBuf macros.
Converted sites (each verified to neither double-free nor leak on any path):
- flags.cpp: unparse_object_numonly, unparse_object — single-exit producers,
now LBuf + release().
- comsys.cpp: call_mogrifier — LBufPtr + release() (its early nullptr returns
precede the allocation, so they are unaffected).
- functions.cpp: switch_handler / switchall_handler — the ping-pong mbuff/tbuff
scratch buffers now use LBuf RAII, removing six manual free_lbuf() calls
including the error-prone early-return path in switch_handler. mbuff is only
transiently aliased into mudstate.switch_token (restored before scope exit),
so RAII scope-exit freeing preserves its lifetime.
The remaining ~145 manual sites (fargs[] stores, did_it() charge/runout swaps,
cross-function/struct lifetimes) still need per-site structural work; #717 stays
open. Build clean, all 1053 smoke tests pass (the corpus exercises switch()/
case()/unparse_object heavily).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:52:07 -06:00
|
|
|
|
LBuf buf = LBuf_Src("unparse_object_numonly");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (target < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
mux_strncpy(buf, aszSpecialDBRefNames[-target], LBUF_SIZE-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!Good_obj(target))
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(buf, LBUF_SIZE, T("*ILLEGAL*(#%d)"), target);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(buf, LBUF_SIZE, T("%s(#%d)"), PureName(target), target);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
engine: add LBufPtr owning handle; convert a verified subset to RAII (#717)
Incremental progress on the alloc_lbuf/free_lbuf -> LBuf RAII migration, adding
the structural enabler the issue calls for and converting a small, individually
verified set of the harder (ownership-escaping) sites.
alloc.h:
- LBuf gains release() (relinquish ownership to a caller) and reset() (free
now, become empty). release() lets a producer that returns an lbuf use RAII
for its early/error paths and hand the buffer off at the success return.
- New LBufPtr: an owning, nullable, movable handle. Unlike LBuf (which always
allocates and suits scoped scratch), LBufPtr can be empty, reseated, and
released — for conditional ownership, storing an lbuf in a struct/queue
entry, or producing a buffer to return. LBufPtr_Src / LBufPtr_Adopt mirror
the LBuf macros.
Converted sites (each verified to neither double-free nor leak on any path):
- flags.cpp: unparse_object_numonly, unparse_object — single-exit producers,
now LBuf + release().
- comsys.cpp: call_mogrifier — LBufPtr + release() (its early nullptr returns
precede the allocation, so they are unaffected).
- functions.cpp: switch_handler / switchall_handler — the ping-pong mbuff/tbuff
scratch buffers now use LBuf RAII, removing six manual free_lbuf() calls
including the error-prone early-return path in switch_handler. mbuff is only
transiently aliased into mudstate.switch_token (restored before scope exit),
so RAII scope-exit freeing preserves its lifetime.
The remaining ~145 manual sites (fargs[] stores, did_it() charge/runout swaps,
cross-function/struct lifetimes) still need per-site structural work; #717 stays
open. Build clean, all 1053 smoke tests pass (the corpus exercises switch()/
case()/unparse_object heavily).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:52:07 -06:00
|
|
|
|
return buf.release();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * Return an lbuf pointing to the object name and possibly the db# and flags
|
|
|
|
|
|
*/
|
2021-02-24 16:17:42 -07:00
|
|
|
|
UTF8 *unparse_object(dbref player, dbref target, bool obey_myopic)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
engine: add LBufPtr owning handle; convert a verified subset to RAII (#717)
Incremental progress on the alloc_lbuf/free_lbuf -> LBuf RAII migration, adding
the structural enabler the issue calls for and converting a small, individually
verified set of the harder (ownership-escaping) sites.
alloc.h:
- LBuf gains release() (relinquish ownership to a caller) and reset() (free
now, become empty). release() lets a producer that returns an lbuf use RAII
for its early/error paths and hand the buffer off at the success return.
- New LBufPtr: an owning, nullable, movable handle. Unlike LBuf (which always
allocates and suits scoped scratch), LBufPtr can be empty, reseated, and
released — for conditional ownership, storing an lbuf in a struct/queue
entry, or producing a buffer to return. LBufPtr_Src / LBufPtr_Adopt mirror
the LBuf macros.
Converted sites (each verified to neither double-free nor leak on any path):
- flags.cpp: unparse_object_numonly, unparse_object — single-exit producers,
now LBuf + release().
- comsys.cpp: call_mogrifier — LBufPtr + release() (its early nullptr returns
precede the allocation, so they are unaffected).
- functions.cpp: switch_handler / switchall_handler — the ping-pong mbuff/tbuff
scratch buffers now use LBuf RAII, removing six manual free_lbuf() calls
including the error-prone early-return path in switch_handler. mbuff is only
transiently aliased into mudstate.switch_token (restored before scope exit),
so RAII scope-exit freeing preserves its lifetime.
The remaining ~145 manual sites (fargs[] stores, did_it() charge/runout swaps,
cross-function/struct lifetimes) still need per-site structural work; #717 stays
open. Build clean, all 1053 smoke tests pass (the corpus exercises switch()/
case()/unparse_object heavily).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:52:07 -06:00
|
|
|
|
LBuf buf = LBuf_Src("unparse_object");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (NOPERM <= target && target < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
mux_strncpy(buf, aszSpecialDBRefNames[-target], LBUF_SIZE-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!Good_obj(target))
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(buf, LBUF_SIZE, T("*ILLEGAL*(#%d)"), target);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bool exam;
|
|
|
|
|
|
if (obey_myopic)
|
|
|
|
|
|
{
|
|
|
|
|
|
exam = MyopicExam(player, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
exam = Examinable(player, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-10-27 17:12:57 +00:00
|
|
|
|
// Leave and extra 100 bytes for the dbref and flags at the end and
|
|
|
|
|
|
// color at the beginning if necessary..
|
|
|
|
|
|
//
|
2007-03-23 00:39:01 +00:00
|
|
|
|
mux_field fldName = StripTabsAndTruncate( Moniker(target), buf,
|
|
|
|
|
|
LBUF_SIZE-100, LBUF_SIZE-100);
|
|
|
|
|
|
UTF8 *bp = buf + fldName.m_byte;
|
2006-11-22 22:02:12 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( exam
|
|
|
|
|
|
|| (Flags(target) & (CHOWN_OK | JUMP_OK | LINK_OK | DESTROY_OK))
|
|
|
|
|
|
|| (Flags2(target) & ABODE))
|
|
|
|
|
|
{
|
2006-10-27 17:12:57 +00:00
|
|
|
|
// Show everything.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *fp = decode_flags(player, &(db[target].fs));
|
2006-10-02 00:42:46 +00:00
|
|
|
|
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_str(T("(#"), buf, &bp);
|
2006-10-02 00:42:46 +00:00
|
|
|
|
safe_ltoa(target, buf, &bp);
|
|
|
|
|
|
safe_str(fp, buf, &bp);
|
|
|
|
|
|
safe_chr(')', buf, &bp);
|
2006-11-22 22:02:12 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
free_sbuf(fp);
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
*bp = '\0';
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
engine: add LBufPtr owning handle; convert a verified subset to RAII (#717)
Incremental progress on the alloc_lbuf/free_lbuf -> LBuf RAII migration, adding
the structural enabler the issue calls for and converting a small, individually
verified set of the harder (ownership-escaping) sites.
alloc.h:
- LBuf gains release() (relinquish ownership to a caller) and reset() (free
now, become empty). release() lets a producer that returns an lbuf use RAII
for its early/error paths and hand the buffer off at the success return.
- New LBufPtr: an owning, nullable, movable handle. Unlike LBuf (which always
allocates and suits scoped scratch), LBufPtr can be empty, reseated, and
released — for conditional ownership, storing an lbuf in a struct/queue
entry, or producing a buffer to return. LBufPtr_Src / LBufPtr_Adopt mirror
the LBuf macros.
Converted sites (each verified to neither double-free nor leak on any path):
- flags.cpp: unparse_object_numonly, unparse_object — single-exit producers,
now LBuf + release().
- comsys.cpp: call_mogrifier — LBufPtr + release() (its early nullptr returns
precede the allocation, so they are unaffected).
- functions.cpp: switch_handler / switchall_handler — the ping-pong mbuff/tbuff
scratch buffers now use LBuf RAII, removing six manual free_lbuf() calls
including the error-prone early-return path in switch_handler. mbuff is only
transiently aliased into mudstate.switch_token (restored before scope exit),
so RAII scope-exit freeing preserves its lifetime.
The remaining ~145 manual sites (fargs[] stores, did_it() charge/runout swaps,
cross-function/struct lifetimes) still need per-site structural work; #717 stays
open. Build clean, all 1053 smoke tests pass (the corpus exercises switch()/
case()/unparse_object heavily).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:52:07 -06:00
|
|
|
|
return buf.release();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-10-27 17:12:57 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* cf_flag_access: Modify who can set a flag.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
CF_HAND(cf_flag_access)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(vp);
|
|
|
|
|
|
UNUSED_PARAMETER(pExtra);
|
|
|
|
|
|
UNUSED_PARAMETER(nExtra);
|
|
|
|
|
|
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(str, T(" \t=,"));
|
|
|
|
|
|
UTF8 *fstr = st.parse();
|
|
|
|
|
|
UTF8 *permstr = st.parse();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ( nullptr == fstr
|
2007-09-14 08:40:03 -07:00
|
|
|
|
|| '\0' == fstr[0]
|
2018-10-03 17:54:51 +00:00
|
|
|
|
|| nullptr == permstr
|
2007-09-14 08:40:03 -07:00
|
|
|
|
|| '\0' == permstr[0])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ((fp = find_flag(fstr)) == nullptr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
cf_log_notfound(player, cmd, T("No such flag"), fstr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
|
|
|
|
|
|
// Don't change the handlers on special things.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( (fbe->handler != fh_any)
|
|
|
|
|
|
&& (fbe->handler != fh_wizroy)
|
|
|
|
|
|
&& (fbe->handler != fh_wiz)
|
|
|
|
|
|
&& (fbe->handler != fh_god)
|
|
|
|
|
|
&& (fbe->handler != fh_restrict_player)
|
|
|
|
|
|
&& (fbe->handler != fh_privileged))
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_CONFIGMODS, "CFG", "PERM");
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T("Cannot change access for flag: "));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
log_text(fp->flagname);
|
|
|
|
|
|
ENDLOG;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(player, M_("Special flags cannot be modified with flag_access."));
|
2007-12-14 01:46:31 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-12-14 01:46:31 +00:00
|
|
|
|
bool negate = false;
|
|
|
|
|
|
|
2007-12-15 15:56:22 -08:00
|
|
|
|
if ('!' == *permstr)
|
2007-12-14 01:46:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
negate = true;
|
2007-12-15 15:56:22 -08:00
|
|
|
|
permstr++;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:39:35 -07:00
|
|
|
|
if (!strcmp(reinterpret_cast<char *>(permstr), "any"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_any;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "dark"))
|
2007-12-14 01:46:31 +00:00
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
if (negate)
|
2007-12-14 01:46:31 +00:00
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
fbe->listperm &= ~CA_GOD;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
fbe->listperm |= CA_GOD;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "hidden"))
|
2007-12-14 01:46:31 +00:00
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
if (negate)
|
2007-12-14 01:46:31 +00:00
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
fbe->listperm &= ~CA_WIZARD;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-12-15 15:56:22 -08:00
|
|
|
|
fbe->listperm |= CA_WIZARD;
|
2007-12-14 01:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "royalty"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_wizroy;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "wizard"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_wiz;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "god"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_god;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "restrict_player"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_restrict_player;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "privileged"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_privileged;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(permstr), "staff"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
fbe->handler = fh_staff;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
cf_log_notfound(player, cmd, T("Flag access"), permstr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * convert_flags: convert a list of flag letters into its bit pattern.
|
|
|
|
|
|
* * Also set the type qualifier if specified and not already set.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
bool convert_flags(dbref player, UTF8 *flaglist, FLAGSET *fset, FLAG *p_type)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
FLAG type = NOTYPE;
|
|
|
|
|
|
FLAGSET flagmask;
|
|
|
|
|
|
memset(&flagmask, 0, sizeof(flagmask));
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *s;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool handled;
|
|
|
|
|
|
for (s = flaglist; *s; s++)
|
|
|
|
|
|
{
|
|
|
|
|
|
handled = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Check for object type.
|
|
|
|
|
|
//
|
|
|
|
|
|
for (i = 0; i <= 7 && !handled; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( object_types[i].lett == *s
|
|
|
|
|
|
&& !( ( (object_types[i].perm & CA_STAFF)
|
|
|
|
|
|
&& !Staff(player))
|
|
|
|
|
|
|| ( (object_types[i].perm & CA_ADMIN)
|
|
|
|
|
|
&& !WizRoy(player))
|
|
|
|
|
|
|| ( (object_types[i].perm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (object_types[i].perm & CA_GOD)
|
|
|
|
|
|
&& !God(player))))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( type != NOTYPE
|
|
|
|
|
|
&& type != i)
|
|
|
|
|
|
{
|
nls: mark object/flags/boolexp create-and-flag notify prose with M_()
Phase 3 coverage slice. Player and staff notifies that still used T()
(cast-only) now use M_() so they extract into the catalogue:
object name taken/silly, deposit refund, @chown summary, parent/zone/
home/dropto clears, floating room
flags Flags: header, type/flag parse errors, @set decompile, flag
name removed
boolexp match “don’t see / which”
wiz password changed by %s
player @protect all listing line
Left alone: log messages, HTML, softcode/machine tokens, concatenated
fragments (set @chown owner line, rob give pieces), and punctuation
separators. pot 704 -> 722; xx filled; ko msgmerge only.
2026-07-28 13:06:50 +00:00
|
|
|
|
UTF8 *p = tprintf(M_("%c: Conflicting type specifications."),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*s);
|
|
|
|
|
|
notify(player, p);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
type = i;
|
|
|
|
|
|
handled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check generic flags.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (handled)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
|
|
|
|
|
for (fp = gen_flag_names; fp->flagname && !handled; fp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
if ( !fp->bPositive
|
|
|
|
|
|
|| fbe->flaglett == ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( fbe->flaglett == *s
|
|
|
|
|
|
&& !( ( (fbe->listperm & CA_STAFF)
|
|
|
|
|
|
&& !Staff(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_ADMIN)
|
|
|
|
|
|
&& !WizRoy(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_WIZARD)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
|| ( (fbe->listperm & CA_GOD)
|
|
|
|
|
|
&& !God(player))))
|
|
|
|
|
|
{
|
|
|
|
|
|
flagmask.word[fbe->flagflag] |= fbe->flagvalue;
|
|
|
|
|
|
handled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!handled)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player,
|
nls: mark object/flags/boolexp create-and-flag notify prose with M_()
Phase 3 coverage slice. Player and staff notifies that still used T()
(cast-only) now use M_() so they extract into the catalogue:
object name taken/silly, deposit refund, @chown summary, parent/zone/
home/dropto clears, floating room
flags Flags: header, type/flag parse errors, @set decompile, flag
name removed
boolexp match “don’t see / which”
wiz password changed by %s
player @protect all listing line
Left alone: log messages, HTML, softcode/machine tokens, concatenated
fragments (set @chown owner line, rob give pieces), and punctuation
separators. pot 704 -> 722; xx filled; ko msgmerge only.
2026-07-28 13:06:50 +00:00
|
|
|
|
tprintf(M_("%c: Flag unknown or not valid for specified object type"),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*s));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return flags to search for and type.
|
|
|
|
|
|
//
|
|
|
|
|
|
*fset = flagmask;
|
|
|
|
|
|
*p_type = type;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * decompile_flags: Produce commands to set flags on target.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
void decompile_flags(dbref player, dbref thing, UTF8 *thingname)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Report generic flags.
|
|
|
|
|
|
//
|
|
|
|
|
|
FLAGNAMEENT *fp;
|
|
|
|
|
|
for (fp = gen_flag_names; fp->flagname; fp++)
|
|
|
|
|
|
{
|
|
|
|
|
|
FLAGBITENT *fbe = fp->fbe;
|
|
|
|
|
|
|
|
|
|
|
|
// Only handle positive-sense entries.
|
|
|
|
|
|
// Skip if we shouldn't decompile this flag.
|
|
|
|
|
|
// Skip if this flag isn't set.
|
|
|
|
|
|
// Skip if we can't see this flag.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !fp->bPositive
|
|
|
|
|
|
|| (fbe->listperm & CA_NO_DECOMP)
|
|
|
|
|
|
|| (db[thing].fs.word[fbe->flagflag] & fbe->flagvalue) == 0
|
|
|
|
|
|
|| !check_access(player, fbe->listperm))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Report this flag.
|
|
|
|
|
|
//
|
2026-07-28 13:57:48 +00:00
|
|
|
|
notify(player, tprintf(T("@set %s=%s"), thingname, fp->flagname));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// do_flag: Rename flags or remove flag aliases.
|
|
|
|
|
|
// Based on RhostMUSH code.
|
|
|
|
|
|
//
|
2022-03-18 23:33:03 -06:00
|
|
|
|
static bool flag_rename(UTF8 *existing_flag_name, UTF8 *new_flag_name)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
int existing_flag_name_length;
|
|
|
|
|
|
bool valid;
|
|
|
|
|
|
UTF8 *canonical_existing_flag_name = MakeCanonicalFlagName(existing_flag_name, &existing_flag_name_length, &valid);
|
|
|
|
|
|
if (valid)
|
|
|
|
|
|
{
|
|
|
|
|
|
const vector<UTF8> existing_name(canonical_existing_flag_name, canonical_existing_flag_name + existing_flag_name_length);
|
|
|
|
|
|
const auto it_existing = mudstate.flag_names_map.find(existing_name);
|
|
|
|
|
|
if (it_existing != mudstate.flag_names_map.end())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
int new_flag_name_length;
|
|
|
|
|
|
UTF8* canonical_new_flag_name = MakeCanonicalFlagName(new_flag_name, &new_flag_name_length, &valid);
|
|
|
|
|
|
if (valid)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
vector<UTF8> new_name(canonical_new_flag_name, canonical_new_flag_name + new_flag_name_length);
|
|
|
|
|
|
const auto it_new = mudstate.flag_names_map.find(new_name);
|
2026-03-03 21:01:02 -07:00
|
|
|
|
if ( it_new != mudstate.flag_names_map.end()
|
|
|
|
|
|
&& it_new->second == it_existing->second)
|
|
|
|
|
|
{
|
|
|
|
|
|
// New name is an alias for the same flag.
|
|
|
|
|
|
// Remove the alias so the rename can proceed.
|
|
|
|
|
|
//
|
|
|
|
|
|
mudstate.flag_names_map.erase(it_new);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (mudstate.flag_names_map.find(new_name) == mudstate.flag_names_map.end())
|
2022-03-18 23:33:03 -06:00
|
|
|
|
{
|
|
|
|
|
|
FLAGNAMEENT* flag_name_entity = it_existing->second;
|
|
|
|
|
|
mudstate.flag_names_map.insert(make_pair(new_name, flag_name_entity));
|
|
|
|
|
|
if (flag_name_entity->flagname != flag_name_entity->pOrigName)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(flag_name_entity->flagname);
|
|
|
|
|
|
}
|
|
|
|
|
|
flag_name_entity->flagname = StringCloneLen(canonical_new_flag_name, new_flag_name_length);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-01 23:47:27 -07:00
|
|
|
|
void do_flag(dbref executor, dbref caller, dbref enactor, int eval, int key, int nargs,
|
2022-03-18 23:33:03 -06:00
|
|
|
|
UTF8 *existing_flag_name, UTF8 *new_flag_name, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (key & FLAG_REMOVE)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nargs == 2)
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Extra argument ignored."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2022-03-18 23:33:03 -06:00
|
|
|
|
int flag_name_length;
|
|
|
|
|
|
bool valid;
|
|
|
|
|
|
UTF8 *canonical_flag_name = MakeCanonicalFlagName(existing_flag_name, &flag_name_length, &valid);
|
|
|
|
|
|
if (valid)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
const vector<UTF8> name(canonical_flag_name, canonical_flag_name + flag_name_length);
|
|
|
|
|
|
const auto it = mudstate.flag_names_map.find(name);
|
|
|
|
|
|
if (it != mudstate.flag_names_map.end())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
FLAGNAMEENT* flag_name_entity = it->second;
|
|
|
|
|
|
|
|
|
|
|
|
if ( flag_name_entity->flagname != flag_name_entity->pOrigName
|
|
|
|
|
|
&& mux_stricmp(flag_name_entity->flagname, canonical_flag_name) == 0)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-18 23:33:03 -06:00
|
|
|
|
MEMFREE(flag_name_entity->flagname);
|
|
|
|
|
|
flag_name_entity->flagname = const_cast<UTF8*>(flag_name_entity->pOrigName);
|
|
|
|
|
|
mudstate.flag_names_map.erase(it);
|
nls: mark object/flags/boolexp create-and-flag notify prose with M_()
Phase 3 coverage slice. Player and staff notifies that still used T()
(cast-only) now use M_() so they extract into the catalogue:
object name taken/silly, deposit refund, @chown summary, parent/zone/
home/dropto clears, floating room
flags Flags: header, type/flag parse errors, @set decompile, flag
name removed
boolexp match “don’t see / which”
wiz password changed by %s
player @protect all listing line
Left alone: log messages, HTML, softcode/machine tokens, concatenated
fragments (set @chown owner line, rob give pieces), and punctuation
separators. pot 704 -> 722; xx filled; ko msgmerge only.
2026-07-28 13:06:50 +00:00
|
|
|
|
notify(executor, tprintf(M_("Flag name ‘%s’ removed from the hash table."), canonical_flag_name));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Error: You can’t remove the present flag name from the hash table."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):
- @clone/cost on an exit bypassed the "must control current location"
check (it lived only on the non-/cost path), letting a builder splice a
cloned exit into a room they do not control. (#855)
- The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
which does not exist (produces "Huh?"); corrected to @mark_all/clear.
(#856)
- @ps <object> reported nothing for a controlled object owned by another
player; do_ps was missing the non-player-target else clause that the
sibling @halt has (clear the owner filter). (#857)
- whisper "<quoted name>" skipped the locality/connected gate the unquoted
form applies, giving a success confirmation plus a delivery error and
polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
did not advance the parser. (#858)
- @flag/remove of an unknown/empty flag name was silent; now reports an
error like the other flag-name failure paths. (#859)
- report help said 8-hour segments but the code uses 4 (deliberately, per
4a845139f); corrected the help. (#860)
Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Error: Bad flagname given or flag not found."));
|
Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):
- @clone/cost on an exit bypassed the "must control current location"
check (it lived only on the non-/cost path), letting a builder splice a
cloned exit into a room they do not control. (#855)
- The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
which does not exist (produces "Huh?"); corrected to @mark_all/clear.
(#856)
- @ps <object> reported nothing for a controlled object owned by another
player; do_ps was missing the non-player-target else clause that the
sibling @halt has (clear the owner filter). (#857)
- whisper "<quoted name>" skipped the locality/connected gate the unquoted
form applies, giving a success confirmation plus a delivery error and
polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
did not advance the parser. (#858)
- @flag/remove of an unknown/empty flag name was silent; now reports an
error like the other flag-name failure paths. (#859)
- report help said 8-hour segments but the code uses 4 (deliberately, per
4a845139f); corrected the help. (#860)
Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Error: Bad flagname given or flag not found."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nargs < 2)
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("You must specify a flag and a name."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-03-18 23:33:03 -06:00
|
|
|
|
if (flag_rename(existing_flag_name, new_flag_name))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Flag name changed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 10:49:10 +00:00
|
|
|
|
notify(executor, M_("Error: Bad flagname given or flag not found."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* cf_flag_name: Rename a flag. Counterpart to @flag.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
CF_HAND(cf_flag_name)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(vp);
|
|
|
|
|
|
UNUSED_PARAMETER(pExtra);
|
|
|
|
|
|
UNUSED_PARAMETER(nExtra);
|
|
|
|
|
|
UNUSED_PARAMETER(player);
|
|
|
|
|
|
UNUSED_PARAMETER(cmd);
|
|
|
|
|
|
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(str, T(" \t=,"));
|
|
|
|
|
|
UTF8 *flagstr = st.parse();
|
|
|
|
|
|
UTF8 *namestr = st.parse();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( !flagstr
|
|
|
|
|
|
|| !*flagstr
|
|
|
|
|
|
|| !namestr
|
|
|
|
|
|
|| !*namestr)
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (flag_rename(flagstr, namestr))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|