2007-01-03 03:11:53 +00:00
|
|
|
|
/*! \file predicates.cpp
|
|
|
|
|
|
* \brief Miscellaneous commands and functions.
|
|
|
|
|
|
*
|
|
|
|
|
|
* In theory, most of these functions could plausibly be called
|
|
|
|
|
|
* "predicates", either because they determine some boolean property
|
|
|
|
|
|
* of the input, or because they perform some action that makes them
|
|
|
|
|
|
* verb-like. In practice, this is a miscellany.
|
|
|
|
|
|
*/
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
#include "copyright.h"
|
|
|
|
|
|
#include "autoconf.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include "externs.h"
|
2026-07-28 11:37:23 -06:00
|
|
|
|
#include "mux_table.h"
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
Integrate Ragel co_* into libmux.so; replace mux_string case mapping
Build integration:
- color_ops.rl and unicode_tables_c.h added to mux/lib/ and mux/include/
- Makefile.am: C compilation rule for Ragel-generated color_ops.c,
links into libmux.so alongside existing C++ objects
- Tables shared: co_* functions reference the existing DFA tables from
utf8tables.cpp via extern declarations (no duplication)
Function replacements:
- fun_lcstr: mux_string->LowerCase() → co_tolower() single-pass
- fun_ucstr: mux_string->UpperCase() → co_toupper() single-pass
- fun_capstr: mux_string->UpperCaseFirst() → co_totitle() single-pass
- predicates.cpp: LowerCase() for @addcommand → co_tolower()
- mail.cpp: UpperCase() for folder names → co_toupper() (2 sites)
Each replacement eliminates: heap allocation, mux_string import
(strip color → m_vcs vector), per-code-point DFA walk with
replace_Chars(), export_TextColor (re-inject color), destructor.
Replaced by: one co_* call, stack buffer, single Ragel pass.
Dead code removal:
- mux_string::UpperCase() — 0 remaining callers, 44 lines
- mux_string::LowerCase() — 0 remaining callers, 44 lines
- mux_string::UpperCaseFirst() — 0 remaining callers, 44 lines
592 smoke tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:21:10 -06:00
|
|
|
|
extern "C" {
|
|
|
|
|
|
#include "color_ops.h"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 21:44:12 -07:00
|
|
|
|
static inline const UTF8 *utf8_advance_predicate(const UTF8 *p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nullptr == p || '\0' == *p)
|
|
|
|
|
|
{
|
|
|
|
|
|
return p;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t n = utf8_FirstByte[static_cast<unsigned char>(*p)];
|
|
|
|
|
|
if (n < 1 || n >= UTF8_CONTINUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
return p + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (size_t i = 1; i < n; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( '\0' == p[i]
|
|
|
|
|
|
|| UTF8_CONTINUE != utf8_FirstByte[static_cast<unsigned char>(p[i])])
|
|
|
|
|
|
{
|
|
|
|
|
|
return p + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return p + n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 19:40:40 -06:00
|
|
|
|
#include "ganl_stub.h"
|
2026-03-02 22:15:00 -07:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* insert_first, remove_first: Insert or remove objects from lists.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
dbref insert_first(dbref head, dbref thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_Next(thing, head);
|
|
|
|
|
|
return thing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref remove_first(dbref head, dbref thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (head == thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Next(thing);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref prev;
|
|
|
|
|
|
|
|
|
|
|
|
DOLIST(prev, head)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Next(prev) == thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_Next(prev, Next(thing));
|
|
|
|
|
|
return head;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return head;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* reverse_list: Reverse the order of members in a list.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
dbref reverse_list(dbref list)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref newlist, rest;
|
|
|
|
|
|
|
|
|
|
|
|
newlist = NOTHING;
|
|
|
|
|
|
while (list != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
rest = Next(list);
|
|
|
|
|
|
s_Next(list, newlist);
|
|
|
|
|
|
newlist = list;
|
|
|
|
|
|
list = rest;
|
|
|
|
|
|
}
|
|
|
|
|
|
return newlist;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* member - indicate if thing is in list
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
bool member(dbref thing, dbref list)
|
|
|
|
|
|
{
|
|
|
|
|
|
DOLIST(list, list)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (list == thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool could_doit(dbref player, dbref thing, int locknum)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (thing == HOME)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If nonplayer tries to get key, then no.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !isPlayer(player)
|
|
|
|
|
|
&& Key(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Pass_Locks(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf key = LBuf_Adopt(atr_get("could_doit.134", thing, locknum, &aowner, &aflags));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool doit = eval_boolexp_atr(player, thing, thing, key);
|
|
|
|
|
|
return doit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool can_see(dbref player, dbref thing, bool can_see_loc)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Don't show if all the following apply: Sleeping players should not be
|
|
|
|
|
|
// seen. The thing is a disconnected player. The player is not a
|
|
|
|
|
|
// puppet.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( mudconf.dark_sleepers
|
|
|
|
|
|
&& isPlayer(thing)
|
|
|
|
|
|
&& !Connected(thing)
|
|
|
|
|
|
&& !Puppet(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// You don't see yourself or exits.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( player == thing
|
|
|
|
|
|
|| isExit(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-01-18 14:04:32 -08:00
|
|
|
|
// To be visible, light must come from either the location (can_see_loc)
|
|
|
|
|
|
// or the object itself (Light(thing)). This light is then blocked
|
|
|
|
|
|
// by the object itself being dark (it blocked its own light), by not
|
|
|
|
|
|
// passing the visibility lock, or by being in a different reality.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2008-01-18 14:04:32 -08:00
|
|
|
|
// The exception to the above is mudconf.see_own_dark which allows a
|
|
|
|
|
|
// myopic self-examination.
|
|
|
|
|
|
//
|
|
|
|
|
|
return ( ( ( can_see_loc
|
|
|
|
|
|
|| Light(thing))
|
|
|
|
|
|
&& !Dark(thing)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#ifdef REALITY_LVLS
|
2008-01-18 14:04:32 -08:00
|
|
|
|
&& IsReal(player, thing)
|
2006-11-27 05:25:42 +00:00
|
|
|
|
#endif // REALITY_LVLS
|
2009-03-04 13:59:34 -08:00
|
|
|
|
&& could_doit(player, thing, A_LVISIBLE))
|
2008-01-18 14:04:32 -08:00
|
|
|
|
|| ( mudconf.see_own_dark
|
|
|
|
|
|
&& MyopicExam(player, thing)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
fix(win32): clamp A_RQUOTA instead of narrowing it through mux_ltoa (#1408)
add_quota passed mux_atoi64's int64_t straight into mux_ltoa, which takes
long. A no-op on LP64; on LLP64 the high word was simply dropped.
Measured on Win64, quotas enabled, one object refunding one quota:
RQUOTA 8589934592 -> 1 (should be 8589934593)
RQUOTA 4294967296 -> 1 (should be 4294967297)
Both lose the entire high word, and both are correct on Linux, so the
same database gives different quota values depending on the platform that
last touched it.
Clamped rather than widened, which is the decision the issue asked to have
recorded. The quota domain is 32-bit by construction everywhere else:
pay_quota, mung_quotas and do_quota all hold it in int, destroy_obj's
refund is int, and both writers format into UTF8[I32BUF_SIZE]. Widening
add_quota alone would make it the only writer able to store a value every
other reader still truncates, turning a visible error into silent drift.
The stored attribute is only text, and mux_atoi64 parses whatever is in
it, so the clamp is where the domain gets enforced.
Two helpers: quota_from_attr reads A_RQUOTA into the int the rest of the
system expects, and quota_add sums without overflowing (signed overflow
is UB, and INT32_MAX + refund was reachable).
pay_quota is fixed the same way. It had "int quota = mux_atoi64(...)",
which truncates on *every* platform, and I hit it while reproducing this:
a stored 8589934592 silently became -1 there before add_quota ever ran.
That is beyond the issue's literal scope, but it is the same defect in the
same code path, and leaving it means the clamp only half holds. Happy to
split it out if preferred.
After the fix, on Win64:
8589934592 -> 2147483647 clamped, deliberate, platform-independent
4294967296 -> 2147483647
42 -> 43 ordinary values unaffected
-3 -> -2 negatives still work
2147483647 -> 2147483647 saturates instead of overflowing
Because it clamps, behaviour is now identical on LP64 and LLP64.
No smoke test, deliberately. I wrote one and it broke four other cases:
A_RQUOTA is AF_GOD so only #1 can plant the value, but inside the harness
"me" is the test object, so the set silently no-ops; and reaching
add_quota needs mudconf.quotas plus a nonzero thing_quota plus a @dbck,
all of which are server-wide. quotas=1 made permission_paths' object
creation fail and the @dbck perturbed the dolist queue timing. A case
that breaks four others is worth less than none -- #1391's lesson. The
verification above is by hand on Win64 instead.
Smoke: 314 dispatched, 1471 succeeded, 17 failed -- the known
build-configuration failures on this box (exp3, UNIX_DIGEST,
REALITY_LVLS), unchanged from master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:26:57 -06:00
|
|
|
|
// Read A_RQUOTA as the 32-bit quantity the rest of the quota system treats
|
|
|
|
|
|
// it as.
|
|
|
|
|
|
//
|
|
|
|
|
|
// The quota domain is 32-bit by construction everywhere else: pay_quota and
|
|
|
|
|
|
// mung_quotas hold it in int, do_quota parses into int, destroy_obj's refund
|
|
|
|
|
|
// is int, and both writers format into a UTF8[I32BUF_SIZE]. But the stored
|
|
|
|
|
|
// attribute is just text, and mux_atoi64 will parse whatever is in it -- so a
|
|
|
|
|
|
// corrupted or hand-crafted A_RQUOTA delivers a value no caller can represent.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Widening the callers instead was the alternative, and is wrong: it would
|
|
|
|
|
|
// make one writer able to store a value every other reader still truncates,
|
|
|
|
|
|
// which converts a visible error into silent drift. Clamping keeps the
|
|
|
|
|
|
// domain honest and identical on LP64 and LLP64 (#1408).
|
|
|
|
|
|
//
|
|
|
|
|
|
static int quota_from_attr(const UTF8 *pQuota)
|
|
|
|
|
|
{
|
|
|
|
|
|
int64_t q = mux_atoi64(pQuota);
|
|
|
|
|
|
if (q > INT32_MAX)
|
|
|
|
|
|
{
|
|
|
|
|
|
return INT32_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (q < INT32_MIN)
|
|
|
|
|
|
{
|
|
|
|
|
|
return INT32_MIN;
|
|
|
|
|
|
}
|
|
|
|
|
|
return static_cast<int>(q);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add two quota quantities without overflowing int. Both operands are
|
|
|
|
|
|
// already in range; their sum need not be.
|
|
|
|
|
|
//
|
|
|
|
|
|
static int quota_add(int a, int b)
|
|
|
|
|
|
{
|
|
|
|
|
|
int64_t sum = static_cast<int64_t>(a) + static_cast<int64_t>(b);
|
|
|
|
|
|
if (sum > INT32_MAX)
|
|
|
|
|
|
{
|
|
|
|
|
|
return INT32_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sum < INT32_MIN)
|
|
|
|
|
|
{
|
|
|
|
|
|
return INT32_MIN;
|
|
|
|
|
|
}
|
|
|
|
|
|
return static_cast<int>(sum);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static bool pay_quota(dbref who, int cost)
|
|
|
|
|
|
{
|
|
|
|
|
|
// If no cost, succeed
|
|
|
|
|
|
//
|
|
|
|
|
|
if (cost <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// determine quota
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf quota_str = LBuf_Adopt(atr_get("pay_quota.200", Owner(who), A_RQUOTA, &aowner, &aflags));
|
fix(win32): clamp A_RQUOTA instead of narrowing it through mux_ltoa (#1408)
add_quota passed mux_atoi64's int64_t straight into mux_ltoa, which takes
long. A no-op on LP64; on LLP64 the high word was simply dropped.
Measured on Win64, quotas enabled, one object refunding one quota:
RQUOTA 8589934592 -> 1 (should be 8589934593)
RQUOTA 4294967296 -> 1 (should be 4294967297)
Both lose the entire high word, and both are correct on Linux, so the
same database gives different quota values depending on the platform that
last touched it.
Clamped rather than widened, which is the decision the issue asked to have
recorded. The quota domain is 32-bit by construction everywhere else:
pay_quota, mung_quotas and do_quota all hold it in int, destroy_obj's
refund is int, and both writers format into UTF8[I32BUF_SIZE]. Widening
add_quota alone would make it the only writer able to store a value every
other reader still truncates, turning a visible error into silent drift.
The stored attribute is only text, and mux_atoi64 parses whatever is in
it, so the clamp is where the domain gets enforced.
Two helpers: quota_from_attr reads A_RQUOTA into the int the rest of the
system expects, and quota_add sums without overflowing (signed overflow
is UB, and INT32_MAX + refund was reachable).
pay_quota is fixed the same way. It had "int quota = mux_atoi64(...)",
which truncates on *every* platform, and I hit it while reproducing this:
a stored 8589934592 silently became -1 there before add_quota ever ran.
That is beyond the issue's literal scope, but it is the same defect in the
same code path, and leaving it means the clamp only half holds. Happy to
split it out if preferred.
After the fix, on Win64:
8589934592 -> 2147483647 clamped, deliberate, platform-independent
4294967296 -> 2147483647
42 -> 43 ordinary values unaffected
-3 -> -2 negatives still work
2147483647 -> 2147483647 saturates instead of overflowing
Because it clamps, behaviour is now identical on LP64 and LLP64.
No smoke test, deliberately. I wrote one and it broke four other cases:
A_RQUOTA is AF_GOD so only #1 can plant the value, but inside the harness
"me" is the test object, so the set silently no-ops; and reaching
add_quota needs mudconf.quotas plus a nonzero thing_quota plus a @dbck,
all of which are server-wide. quotas=1 made permission_paths' object
creation fail and the @dbck perturbed the dolist queue timing. A case
that breaks four others is worth less than none -- #1391's lesson. The
verification above is by hand on Win64 instead.
Smoke: 314 dispatched, 1471 succeeded, 17 failed -- the known
build-configuration failures on this box (exp3, UNIX_DIGEST,
REALITY_LVLS), unchanged from master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:26:57 -06:00
|
|
|
|
int quota = quota_from_attr(quota_str);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// enough to build? Wizards always have enough.
|
|
|
|
|
|
//
|
fix(win32): clamp A_RQUOTA instead of narrowing it through mux_ltoa (#1408)
add_quota passed mux_atoi64's int64_t straight into mux_ltoa, which takes
long. A no-op on LP64; on LLP64 the high word was simply dropped.
Measured on Win64, quotas enabled, one object refunding one quota:
RQUOTA 8589934592 -> 1 (should be 8589934593)
RQUOTA 4294967296 -> 1 (should be 4294967297)
Both lose the entire high word, and both are correct on Linux, so the
same database gives different quota values depending on the platform that
last touched it.
Clamped rather than widened, which is the decision the issue asked to have
recorded. The quota domain is 32-bit by construction everywhere else:
pay_quota, mung_quotas and do_quota all hold it in int, destroy_obj's
refund is int, and both writers format into UTF8[I32BUF_SIZE]. Widening
add_quota alone would make it the only writer able to store a value every
other reader still truncates, turning a visible error into silent drift.
The stored attribute is only text, and mux_atoi64 parses whatever is in
it, so the clamp is where the domain gets enforced.
Two helpers: quota_from_attr reads A_RQUOTA into the int the rest of the
system expects, and quota_add sums without overflowing (signed overflow
is UB, and INT32_MAX + refund was reachable).
pay_quota is fixed the same way. It had "int quota = mux_atoi64(...)",
which truncates on *every* platform, and I hit it while reproducing this:
a stored 8589934592 silently became -1 there before add_quota ever ran.
That is beyond the issue's literal scope, but it is the same defect in the
same code path, and leaving it means the clamp only half holds. Happy to
split it out if preferred.
After the fix, on Win64:
8589934592 -> 2147483647 clamped, deliberate, platform-independent
4294967296 -> 2147483647
42 -> 43 ordinary values unaffected
-3 -> -2 negatives still work
2147483647 -> 2147483647 saturates instead of overflowing
Because it clamps, behaviour is now identical on LP64 and LLP64.
No smoke test, deliberately. I wrote one and it broke four other cases:
A_RQUOTA is AF_GOD so only #1 can plant the value, but inside the harness
"me" is the test object, so the set silently no-ops; and reaching
add_quota needs mudconf.quotas plus a nonzero thing_quota plus a @dbck,
all of which are server-wide. quotas=1 made permission_paths' object
creation fail and the @dbck perturbed the dolist queue timing. A case
that breaks four others is worth less than none -- #1391's lesson. The
verification above is by hand on Win64 instead.
Smoke: 314 dispatched, 1471 succeeded, 17 failed -- the known
build-configuration failures on this box (exp3, UNIX_DIGEST,
REALITY_LVLS), unchanged from master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:26:57 -06:00
|
|
|
|
quota = quota_add(quota, -cost);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( quota < 0
|
|
|
|
|
|
&& !Free_Quota(who)
|
|
|
|
|
|
&& !Free_Quota(Owner(who)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Dock the quota.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 buf[I32BUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
mux_ltoa(quota, buf);
|
|
|
|
|
|
atr_add_raw(Owner(who), A_RQUOTA, buf);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool canpayfees(dbref player, dbref who, int pennies, int quota)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !Wizard(who)
|
|
|
|
|
|
&& !Wizard(Owner(who))
|
|
|
|
|
|
&& !Free_Money(who)
|
|
|
|
|
|
&& !Free_Money(Owner(who))
|
|
|
|
|
|
&& (Pennies(Owner(who)) < pennies))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (player == who)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(player, tprintf(M_("Sorry, you don’t have enough %s."),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
mudconf.many_coins));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(player, tprintf(M_("Sorry, that player doesn’t have enough %s."),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
mudconf.many_coins));
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mudconf.quotas)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!pay_quota(who, quota))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (player == who)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Sorry, your building contract has run out."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player,
|
2026-07-27 12:39:20 +00:00
|
|
|
|
M_("Sorry, that player’s building contract has run out."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
payfor(who, pennies);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool payfor(dbref who, int cost)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Wizard(who)
|
|
|
|
|
|
|| Wizard(Owner(who))
|
|
|
|
|
|
|| Free_Money(who)
|
|
|
|
|
|
|| Free_Money(Owner(who)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
who = Owner(who);
|
|
|
|
|
|
int tmp;
|
|
|
|
|
|
if ((tmp = Pennies(who)) >= cost)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_Pennies(who, tmp - cost);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void add_quota(dbref who, int payment)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 buf[I32BUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf quota = LBuf_Adopt(atr_get("add_quota.288", who, A_RQUOTA, &aowner, &aflags));
|
fix(win32): clamp A_RQUOTA instead of narrowing it through mux_ltoa (#1408)
add_quota passed mux_atoi64's int64_t straight into mux_ltoa, which takes
long. A no-op on LP64; on LLP64 the high word was simply dropped.
Measured on Win64, quotas enabled, one object refunding one quota:
RQUOTA 8589934592 -> 1 (should be 8589934593)
RQUOTA 4294967296 -> 1 (should be 4294967297)
Both lose the entire high word, and both are correct on Linux, so the
same database gives different quota values depending on the platform that
last touched it.
Clamped rather than widened, which is the decision the issue asked to have
recorded. The quota domain is 32-bit by construction everywhere else:
pay_quota, mung_quotas and do_quota all hold it in int, destroy_obj's
refund is int, and both writers format into UTF8[I32BUF_SIZE]. Widening
add_quota alone would make it the only writer able to store a value every
other reader still truncates, turning a visible error into silent drift.
The stored attribute is only text, and mux_atoi64 parses whatever is in
it, so the clamp is where the domain gets enforced.
Two helpers: quota_from_attr reads A_RQUOTA into the int the rest of the
system expects, and quota_add sums without overflowing (signed overflow
is UB, and INT32_MAX + refund was reachable).
pay_quota is fixed the same way. It had "int quota = mux_atoi64(...)",
which truncates on *every* platform, and I hit it while reproducing this:
a stored 8589934592 silently became -1 there before add_quota ever ran.
That is beyond the issue's literal scope, but it is the same defect in the
same code path, and leaving it means the clamp only half holds. Happy to
split it out if preferred.
After the fix, on Win64:
8589934592 -> 2147483647 clamped, deliberate, platform-independent
4294967296 -> 2147483647
42 -> 43 ordinary values unaffected
-3 -> -2 negatives still work
2147483647 -> 2147483647 saturates instead of overflowing
Because it clamps, behaviour is now identical on LP64 and LLP64.
No smoke test, deliberately. I wrote one and it broke four other cases:
A_RQUOTA is AF_GOD so only #1 can plant the value, but inside the harness
"me" is the test object, so the set silently no-ops; and reaching
add_quota needs mudconf.quotas plus a nonzero thing_quota plus a @dbck,
all of which are server-wide. quotas=1 made permission_paths' object
creation fail and the @dbck perturbed the dolist queue timing. A case
that breaks four others is worth less than none -- #1391's lesson. The
verification above is by hand on Win64 instead.
Smoke: 314 dispatched, 1471 succeeded, 17 failed -- the known
build-configuration failures on this box (exp3, UNIX_DIGEST,
REALITY_LVLS), unchanged from master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:26:57 -06:00
|
|
|
|
|
|
|
|
|
|
// mux_ltoa takes long, so passing the int64_t from mux_atoi64 directly
|
|
|
|
|
|
// narrowed it -- a no-op on LP64, but on LLP64 the high word was dropped
|
|
|
|
|
|
// and a stored 8589934592 came back as 1 (#1408).
|
|
|
|
|
|
//
|
|
|
|
|
|
mux_ltoa(quota_add(quota_from_attr(quota), payment), buf);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_add_raw(who, A_RQUOTA, buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void giveto(dbref who, int pennies)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Wizard(who)
|
|
|
|
|
|
|| Wizard(Owner(who))
|
|
|
|
|
|
|| Free_Money(who)
|
|
|
|
|
|
|| Free_Money(Owner(who)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
who = Owner(who);
|
|
|
|
|
|
s_Pennies(who, Pennies(who) + pennies);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-12-21 17:46:20 -08:00
|
|
|
|
// Every character in the name must be allowed by one of the character sets mentioned.
|
|
|
|
|
|
// If no character sets are mentions, everything is allowed.
|
|
|
|
|
|
//
|
2008-06-01 06:37:57 +00:00
|
|
|
|
bool IsRestricted(const UTF8 *pName, int charset)
|
2007-12-21 17:46:20 -08:00
|
|
|
|
{
|
|
|
|
|
|
if (0 == charset)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while ('\0' != pName[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
bool bAllowed = false;
|
|
|
|
|
|
if ( (ALLOW_CHARSET_ASCII & charset)
|
|
|
|
|
|
&& (0x80 & pName[0]) == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
2007-12-23 12:16:23 -08:00
|
|
|
|
else if ( (ALLOW_CHARSET_8859_1 & charset)
|
|
|
|
|
|
&& mux_is8859_1(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
2008-03-22 21:37:35 -07:00
|
|
|
|
else if ( (ALLOW_CHARSET_8859_2 & charset)
|
|
|
|
|
|
&& mux_is8859_2(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
2013-02-14 08:16:05 -07:00
|
|
|
|
else if ( (ALLOW_CHARSET_HANGUL & charset)
|
|
|
|
|
|
&& mux_ishangul(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
2013-08-21 13:11:54 -06:00
|
|
|
|
else if ( (ALLOW_CHARSET_HIRAGANA & charset)
|
|
|
|
|
|
&& mux_ishiragana(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( (ALLOW_CHARSET_KANJI & charset)
|
|
|
|
|
|
&& mux_iskanji(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( (ALLOW_CHARSET_KATAKANA & charset)
|
|
|
|
|
|
&& mux_iskatakana(pName))
|
|
|
|
|
|
{
|
|
|
|
|
|
bAllowed = true;
|
|
|
|
|
|
}
|
2007-12-21 17:46:20 -08:00
|
|
|
|
|
|
|
|
|
|
if (!bAllowed)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-05 21:44:12 -07:00
|
|
|
|
pName = utf8_advance_predicate(pName);
|
2007-12-21 17:46:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// The following function validates that the object names (which will be
|
|
|
|
|
|
// used for things and rooms, but not for players or exits) and generates
|
|
|
|
|
|
// a canonical form of that name (with optimized ANSI).
|
|
|
|
|
|
//
|
2007-12-21 17:46:20 -08:00
|
|
|
|
UTF8 *MakeCanonicalObjectName(const UTF8 *pName, size_t *pnName, bool *pbValid, int charset)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
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 Buf[MBUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pName)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build up what the real name would be. If we pass all the
|
|
|
|
|
|
// checks, this is what we will return as a result.
|
|
|
|
|
|
//
|
2007-03-23 00:39:01 +00:00
|
|
|
|
mux_field fldLen = StripTabsAndTruncate(pName, Buf, MBUF_SIZE-1, MBUF_SIZE-1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Disallow pure ANSI names. There must be at least -something-
|
|
|
|
|
|
// visible.
|
|
|
|
|
|
//
|
2007-03-23 00:39:01 +00:00
|
|
|
|
if (0 == fldLen.m_column)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the stripped version (Visible parts without color info).
|
|
|
|
|
|
//
|
|
|
|
|
|
size_t nStripped;
|
2007-03-18 00:25:00 +00:00
|
|
|
|
const UTF8 *pStripped = strip_color(Buf, &nStripped);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
|
|
|
|
|
|
// as the first character, or SPACE as the last character
|
|
|
|
|
|
//
|
2026-03-03 19:38:14 -07:00
|
|
|
|
if ( reinterpret_cast<const UTF8 *>(strchr(reinterpret_cast<const char *>("*!#"), pStripped[0]))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| mux_isspace(pStripped[0])
|
|
|
|
|
|
|| mux_isspace(pStripped[nStripped-1]))
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
|
|
|
|
|
|
// and OR_TOKEN are allowed.
|
|
|
|
|
|
//
|
2007-03-18 00:25:00 +00:00
|
|
|
|
const UTF8 *p = pStripped;
|
2007-03-18 04:38:43 +00:00
|
|
|
|
while ('\0' != *p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-18 00:25:00 +00:00
|
|
|
|
if (!mux_isobjectname(p))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-05 21:44:12 -07:00
|
|
|
|
p = utf8_advance_predicate(p);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Special names are specifically dis-allowed.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( (nStripped == 2 && memcmp("me", pStripped, 2) == 0)
|
|
|
|
|
|
|| (nStripped == 4 && ( memcmp("home", pStripped, 4) == 0
|
|
|
|
|
|
|| memcmp("here", pStripped, 4) == 0)))
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-06-01 06:37:57 +00:00
|
|
|
|
if (IsRestricted(pStripped, charset))
|
2007-12-21 17:46:20 -08:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2007-12-21 17:46:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:43:09 -07:00
|
|
|
|
// Reject names that would be silently truncated.
|
|
|
|
|
|
//
|
|
|
|
|
|
size_t nOrigStripped;
|
|
|
|
|
|
strip_color(pName, &nOrigStripped);
|
|
|
|
|
|
if (nOrigStripped > nStripped)
|
|
|
|
|
|
{
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-23 00:39:01 +00:00
|
|
|
|
*pnName = fldLen.m_byte;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*pbValid = true;
|
|
|
|
|
|
return Buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The following function validates exit names.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *MakeCanonicalExitName(const UTF8 *pName, size_t *pnName, bool *pbValid)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
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 Buf[MBUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
*pbValid = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pName)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:43:09 -07:00
|
|
|
|
mux_strncpy(Buf, pName, MBUF_SIZE - 1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
// Sanitize the input before processing.
|
|
|
|
|
|
//
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(Buf, T(";"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
// Break the exitname down into semi-colon-separated segments. The first
|
|
|
|
|
|
// segment can contain color as it is used for showing the exit, but the
|
|
|
|
|
|
// remaining segments are stripped of color. A valid exitname requires
|
|
|
|
|
|
// at least one (display) segment.
|
|
|
|
|
|
//
|
2007-09-20 22:57:57 +00:00
|
|
|
|
UTF8 *ptr;
|
2026-03-12 21:31:08 -06:00
|
|
|
|
UTF8 clean_buf[MBUF_SIZE];
|
|
|
|
|
|
UTF8 *bp_clean = clean_buf;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool bHaveDisplay = false;
|
2022-03-11 12:36:37 -07:00
|
|
|
|
for (ptr = st.parse(); ptr; ptr = st.parse())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *pTrimmedSegment = nullptr;
|
2007-09-21 08:03:04 -07:00
|
|
|
|
if (bHaveDisplay)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-09-21 08:03:04 -07:00
|
|
|
|
// No color allowed in segments after the first one.
|
|
|
|
|
|
//
|
|
|
|
|
|
UTF8 *pNoColor = strip_color(ptr);
|
|
|
|
|
|
pTrimmedSegment = trim_spaces(pNoColor);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Color allowed in first segment.
|
|
|
|
|
|
//
|
|
|
|
|
|
pTrimmedSegment = trim_spaces(ptr);
|
|
|
|
|
|
}
|
2007-09-20 22:57:57 +00:00
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
// Ignore segments which contained nothing but spaces.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ('\0' != pTrimmedSegment[0])
|
|
|
|
|
|
{
|
2008-03-10 01:28:52 +00:00
|
|
|
|
bool valid = false;
|
|
|
|
|
|
size_t len = 0;
|
2007-09-20 22:57:57 +00:00
|
|
|
|
|
2008-03-10 01:28:52 +00:00
|
|
|
|
UTF8 *pValidSegment = MakeCanonicalObjectName(pTrimmedSegment, &len, &valid, mudconf.exit_name_charset);
|
|
|
|
|
|
if (valid)
|
2007-09-21 08:03:04 -07:00
|
|
|
|
{
|
2008-03-10 01:28:52 +00:00
|
|
|
|
if (bHaveDisplay)
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
safe_mb_chr_ascii(';', clean_buf, &bp_clean);
|
|
|
|
|
|
safe_mb_str(pValidSegment, clean_buf, &bp_clean);
|
2008-03-10 01:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
safe_mb_str(pValidSegment, clean_buf, &bp_clean);
|
2008-03-10 01:28:52 +00:00
|
|
|
|
bHaveDisplay = true;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-01-09 23:42:05 +00:00
|
|
|
|
free_lbuf(pTrimmedSegment);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
pTrimmedSegment = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-12 21:31:08 -06:00
|
|
|
|
*bp_clean = '\0';
|
2007-09-20 22:57:57 +00:00
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
*pbValid = bHaveDisplay;
|
|
|
|
|
|
if (!bHaveDisplay)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-09-20 22:57:57 +00:00
|
|
|
|
*pnName = 0;
|
|
|
|
|
|
return Buf;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-09-20 22:57:57 +00:00
|
|
|
|
|
2026-03-12 21:31:08 -06:00
|
|
|
|
mux_strncpy(Buf, clean_buf, MBUF_SIZE - 1);
|
2007-09-20 22:57:57 +00:00
|
|
|
|
*pnName = mux_strlen(Buf);
|
|
|
|
|
|
|
|
|
|
|
|
return Buf;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The following function validates the player name. ANSI is not
|
|
|
|
|
|
// allowed in player names. However, a player name must satisfy
|
|
|
|
|
|
// the requirements of a regular name as well.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
bool ValidatePlayerName(const UTF8 *pName)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (!pName)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
size_t nName = strlen(reinterpret_cast<const char *>(pName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Verify that name is not empty, but not too long, either.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nName <= 0
|
|
|
|
|
|
|| PLAYER_NAME_LIMIT <= nName)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
|
|
|
|
|
|
// as the first character, or SPACE as the last character
|
|
|
|
|
|
//
|
2026-03-03 19:38:14 -07:00
|
|
|
|
if ( reinterpret_cast<const UTF8 *>(strchr(reinterpret_cast<const char *>("*!#"), pName[0]))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| mux_isspace(pName[0])
|
|
|
|
|
|
|| mux_isspace(pName[nName-1]))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-18 04:19:05 +00:00
|
|
|
|
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
|
|
|
|
|
|
// and OR_TOKEN are allowed.
|
|
|
|
|
|
//
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( mudstate.bStandAlone
|
|
|
|
|
|
|| mudconf.name_spaces)
|
|
|
|
|
|
{
|
2007-03-18 04:19:05 +00:00
|
|
|
|
const UTF8 *p = pName;
|
2007-03-18 04:38:43 +00:00
|
|
|
|
while ('\0' != *p)
|
2007-03-18 04:19:05 +00:00
|
|
|
|
{
|
|
|
|
|
|
if ( !mux_isplayername(p)
|
|
|
|
|
|
&& ' ' != *p)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-05 21:44:12 -07:00
|
|
|
|
p = utf8_advance_predicate(p);
|
2007-03-18 04:19:05 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-03-18 04:19:05 +00:00
|
|
|
|
const UTF8 *p = pName;
|
2008-01-30 09:41:14 -08:00
|
|
|
|
while ('\0' != *p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-18 04:19:05 +00:00
|
|
|
|
if (!mux_isplayername(p))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-03-05 21:44:12 -07:00
|
|
|
|
p = utf8_advance_predicate(p);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Special names are specifically dis-allowed.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( (nName == 2 && memcmp("me", pName, 2) == 0)
|
|
|
|
|
|
|| (nName == 4 && ( memcmp("home", pName, 4) == 0
|
|
|
|
|
|
|| memcmp("here", pName, 4) == 0)))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2007-12-21 17:46:20 -08:00
|
|
|
|
|
2008-06-01 06:37:57 +00:00
|
|
|
|
if (IsRestricted(pName, mudconf.player_name_charset))
|
2007-12-21 17:46:20 -08:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
bool ok_password(const UTF8 *password, const UTF8 **pmsg)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
*pmsg = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (*password == '\0')
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
*pmsg = T("Null passwords are not allowed.");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int num_upper = 0;
|
|
|
|
|
|
int num_special = 0;
|
|
|
|
|
|
int num_lower = 0;
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
const UTF8 *scan = password;
|
2026-03-05 21:44:12 -07:00
|
|
|
|
for ( ; *scan; scan = utf8_advance_predicate(scan))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-02-18 15:14:35 +00:00
|
|
|
|
if ( !mux_isprint(scan)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| mux_isspace(*scan))
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
*pmsg = T("Illegal character in password.");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2007-03-23 17:32:22 +00:00
|
|
|
|
if (mux_isupper_ascii(*scan))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
num_upper++;
|
|
|
|
|
|
}
|
2007-03-23 17:32:22 +00:00
|
|
|
|
else if (mux_islower_ascii(*scan))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
num_lower++;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( *scan != '\''
|
|
|
|
|
|
&& *scan != '-')
|
|
|
|
|
|
{
|
|
|
|
|
|
num_special++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( !mudstate.bStandAlone
|
|
|
|
|
|
&& mudconf.safer_passwords)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (num_upper < 1)
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
*pmsg = T("The password must contain at least one capital letter.");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (num_lower < 1)
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
*pmsg = T("The password must contain at least one lowercase letter.");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (num_special < 1)
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
*pmsg = T("The password must contain at least one number or a symbol other than the apostrophe or dash.");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* handle_ears: Generate the 'grows ears' and 'loses ears' messages.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void handle_ears(dbref thing, bool could_hear, bool can_hear)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (could_hear != can_hear)
|
|
|
|
|
|
{
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf buf = LBuf_Src("handle_ears");
|
|
|
|
|
|
UTF8 *bp = buf.get();
|
2026-03-12 21:31:08 -06:00
|
|
|
|
|
|
|
|
|
|
// Moniker returns PUA-encoded name.
|
|
|
|
|
|
//
|
|
|
|
|
|
const UTF8 *name = Moniker(thing);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (isExit(thing))
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
// Truncate at first semicolon.
|
|
|
|
|
|
//
|
|
|
|
|
|
const UTF8 *semi = (const UTF8 *)strchr((const char *)name, ';');
|
|
|
|
|
|
if (semi)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
safe_copy_buf(name, semi - name, buf, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_str(name, buf, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-12 21:31:08 -06:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_str(name, buf, &bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-04 15:20:40 -07:00
|
|
|
|
const PRONOUN_SET *pg = get_pronoun_set(thing);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (can_hear)
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
safe_tprintf_str(buf, &bp, T(" grow%s ears and can now hear."),
|
|
|
|
|
|
pg->plural ? "" : "s");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
safe_tprintf_str(buf, &bp, T(" lose%s %s ears and become%s deaf."),
|
|
|
|
|
|
pg->plural ? "" : "s", pg->possessive,
|
|
|
|
|
|
pg->plural ? "" : "s");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-12 21:31:08 -06:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify_check(thing, thing, buf, MSG_ME | MSG_NBR | MSG_LOC | MSG_INV);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For lack of better place the @switch code is here.
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_switch
|
|
|
|
|
|
(
|
2007-04-09 23:40:23 +00:00
|
|
|
|
dbref executor, dbref caller, dbref enactor,
|
|
|
|
|
|
int eval, int key,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *expr,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
UTF8 *args[], int nargs,
|
|
|
|
|
|
const UTF8 *cargs[], int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !expr
|
|
|
|
|
|
|| nargs <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bMatchOne;
|
|
|
|
|
|
switch (key & SWITCH_MASK)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SWITCH_DEFAULT:
|
|
|
|
|
|
if (mudconf.switch_df_all)
|
|
|
|
|
|
{
|
|
|
|
|
|
bMatchOne = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bMatchOne = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SWITCH_ANY:
|
|
|
|
|
|
bMatchOne = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SWITCH_ONE:
|
|
|
|
|
|
default:
|
|
|
|
|
|
bMatchOne = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now try a wild card match of buff with stuff in coms.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bAny = false;
|
|
|
|
|
|
int a;
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf buff = LBuf_Src("do_switch");
|
|
|
|
|
|
UTF8 *bp = buff.get();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
CLinearTimeAbsolute lta;
|
|
|
|
|
|
for ( a = 0;
|
|
|
|
|
|
( !bMatchOne
|
|
|
|
|
|
|| !bAny)
|
|
|
|
|
|
&& a < nargs - 1
|
|
|
|
|
|
&& args[a]
|
|
|
|
|
|
&& args[a + 1];
|
|
|
|
|
|
a += 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
bp = buff;
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(args[a], LBUF_SIZE-1, buff, &bp, executor, caller, enactor, eval|EV_FCHECK|EV_EVAL|EV_TOP,
|
2007-01-28 02:08:08 +00:00
|
|
|
|
cargs, ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
if (wild_match(buff, expr))
|
|
|
|
|
|
{
|
2026-03-07 17:33:26 -07:00
|
|
|
|
const UTF8 *save_switch = mudstate.switch_token;
|
|
|
|
|
|
mudstate.switch_token = expr;
|
2026-03-04 14:46:56 -07:00
|
|
|
|
if (key & SWITCH_NOW)
|
|
|
|
|
|
{
|
2026-03-07 17:33:26 -07:00
|
|
|
|
process_command(executor, caller, enactor, eval, false, args[a+1], cargs, ncargs);
|
2026-03-04 14:46:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-20 11:57:55 -06:00
|
|
|
|
{
|
|
|
|
|
|
// Preserve enclosing iter context (## from @dolist)
|
|
|
|
|
|
// so it survives into the queued @switch body.
|
2026-03-20 12:27:09 -06:00
|
|
|
|
// Respect safer_iter — same guard as @dolist uses.
|
2026-03-20 11:57:55 -06:00
|
|
|
|
//
|
|
|
|
|
|
const UTF8 *iter_tok = nullptr;
|
|
|
|
|
|
int iter_num = 0;
|
2026-03-20 12:27:09 -06:00
|
|
|
|
if (!mudconf.safer_iter)
|
2026-03-20 11:57:55 -06:00
|
|
|
|
{
|
2026-03-20 12:27:09 -06:00
|
|
|
|
int iLoop = mudstate.in_loop - 1;
|
|
|
|
|
|
if ( 0 <= iLoop
|
|
|
|
|
|
&& iLoop < MAX_ITEXT
|
|
|
|
|
|
&& mudstate.itext[iLoop])
|
|
|
|
|
|
{
|
|
|
|
|
|
iter_tok = mudstate.itext[iLoop];
|
|
|
|
|
|
iter_num = mudstate.inum[iLoop];
|
|
|
|
|
|
}
|
2026-03-20 11:57:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, 0,
|
|
|
|
|
|
args[a+1],
|
|
|
|
|
|
ncargs, cargs,
|
|
|
|
|
|
mudstate.global_regs,
|
|
|
|
|
|
nullptr, // named_sargs
|
|
|
|
|
|
iter_tok, iter_num, // iter context
|
|
|
|
|
|
expr); // switch_token
|
|
|
|
|
|
}
|
2026-03-04 14:46:56 -07:00
|
|
|
|
}
|
2026-03-07 17:33:26 -07:00
|
|
|
|
mudstate.switch_token = save_switch;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bAny = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( a < nargs
|
|
|
|
|
|
&& !bAny
|
|
|
|
|
|
&& args[a])
|
|
|
|
|
|
{
|
2026-03-07 17:33:26 -07:00
|
|
|
|
const UTF8 *save_switch = mudstate.switch_token;
|
|
|
|
|
|
mudstate.switch_token = expr;
|
2026-03-04 14:46:56 -07:00
|
|
|
|
if (key & SWITCH_NOW)
|
|
|
|
|
|
{
|
2026-03-07 17:33:26 -07:00
|
|
|
|
process_command(executor, caller, enactor, eval, false, args[a], cargs, ncargs);
|
2026-03-04 14:46:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-20 11:57:55 -06:00
|
|
|
|
{
|
|
|
|
|
|
const UTF8 *iter_tok = nullptr;
|
|
|
|
|
|
int iter_num = 0;
|
2026-03-20 12:27:09 -06:00
|
|
|
|
if (!mudconf.safer_iter)
|
2026-03-20 11:57:55 -06:00
|
|
|
|
{
|
2026-03-20 12:27:09 -06:00
|
|
|
|
int iLoop = mudstate.in_loop - 1;
|
|
|
|
|
|
if ( 0 <= iLoop
|
|
|
|
|
|
&& iLoop < MAX_ITEXT
|
|
|
|
|
|
&& mudstate.itext[iLoop])
|
|
|
|
|
|
{
|
|
|
|
|
|
iter_tok = mudstate.itext[iLoop];
|
|
|
|
|
|
iter_num = mudstate.inum[iLoop];
|
|
|
|
|
|
}
|
2026-03-20 11:57:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, 0,
|
|
|
|
|
|
args[a],
|
|
|
|
|
|
ncargs, cargs,
|
|
|
|
|
|
mudstate.global_regs,
|
|
|
|
|
|
nullptr, // named_sargs
|
|
|
|
|
|
iter_tok, iter_num, // iter context
|
|
|
|
|
|
expr); // switch_token
|
|
|
|
|
|
}
|
2026-03-04 14:46:56 -07:00
|
|
|
|
}
|
2026-03-07 17:33:26 -07:00
|
|
|
|
mudstate.switch_token = save_switch;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (key & SWITCH_NOTIFY)
|
|
|
|
|
|
{
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf tbuf = LBuf_Src("switch.notify_cmd");
|
2007-03-14 22:33:04 +00:00
|
|
|
|
mux_strncpy(tbuf, T("@notify/quiet me"), LBUF_SIZE-1);
|
2006-09-06 05:46:01 +00:00
|
|
|
|
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, A_SEMAPHORE,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
tbuf,
|
|
|
|
|
|
ncargs, cargs,
|
|
|
|
|
|
mudstate.global_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Also for lack of better place the @ifelse code is here.
|
|
|
|
|
|
// Idea for @ifelse from ChaoticMUX.
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_if
|
|
|
|
|
|
(
|
2007-04-09 23:40:23 +00:00
|
|
|
|
dbref player, dbref caller, dbref enactor,
|
|
|
|
|
|
int eval, int key,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *expr,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
UTF8 *args[], int nargs,
|
|
|
|
|
|
const UTF8 *cargs[], int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
|
|
|
|
|
|
if ( !expr
|
|
|
|
|
|
|| nargs <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CLinearTimeAbsolute lta;
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf buff = LBuf_Src("do_if");
|
|
|
|
|
|
UTF8 *bp = buff.get();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(expr, LBUF_SIZE-1, buff, &bp, player, caller, enactor, eval|EV_FCHECK|EV_EVAL|EV_TOP,
|
2007-01-28 02:08:08 +00:00
|
|
|
|
cargs, ncargs);
|
2007-04-14 04:26:38 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int a = !xlate(buff);
|
|
|
|
|
|
|
|
|
|
|
|
if (a < nargs)
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
wait_que(player, caller, enactor, eval, false, lta, NOTHING, 0,
|
|
|
|
|
|
args[a],
|
|
|
|
|
|
ncargs, cargs,
|
|
|
|
|
|
mudstate.global_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_addcommand
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
2007-09-01 23:47:27 -07:00
|
|
|
|
int eval,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *command,
|
|
|
|
|
|
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
|
|
|
|
|
2026-03-04 12:14:01 -07:00
|
|
|
|
bool bNoEval = (key & ADDCMD_NOEVAL) != 0;
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Validate command name.
|
|
|
|
|
|
//
|
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 pName[LBUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (1 <= nargs)
|
|
|
|
|
|
{
|
2026-03-12 21:31:08 -06:00
|
|
|
|
// Strip color, then strip \r\n\t and space.
|
|
|
|
|
|
//
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t nRaw = strlen(reinterpret_cast<const char *>(name));
|
|
|
|
|
|
unsigned char plain[LBUF_SIZE];
|
|
|
|
|
|
size_t nPlain = co_strip_color(plain,
|
|
|
|
|
|
reinterpret_cast<const unsigned char *>(name), nRaw);
|
|
|
|
|
|
size_t j = 0;
|
|
|
|
|
|
for (size_t i = 0; i < nPlain; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (plain[i] != '\r' && plain[i] != '\n'
|
|
|
|
|
|
&& plain[i] != '\t' && plain[i] != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
pName[j++] = plain[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
pName[j] = '\0';
|
|
|
|
|
|
}
|
Integrate Ragel co_* into libmux.so; replace mux_string case mapping
Build integration:
- color_ops.rl and unicode_tables_c.h added to mux/lib/ and mux/include/
- Makefile.am: C compilation rule for Ragel-generated color_ops.c,
links into libmux.so alongside existing C++ objects
- Tables shared: co_* functions reference the existing DFA tables from
utf8tables.cpp via extern declarations (no duplication)
Function replacements:
- fun_lcstr: mux_string->LowerCase() → co_tolower() single-pass
- fun_ucstr: mux_string->UpperCase() → co_toupper() single-pass
- fun_capstr: mux_string->UpperCaseFirst() → co_totitle() single-pass
- predicates.cpp: LowerCase() for @addcommand → co_tolower()
- mail.cpp: UpperCase() for folder names → co_toupper() (2 sites)
Each replacement eliminates: heap allocation, mux_string import
(strip color → m_vcs vector), per-code-point DFA walk with
replace_Chars(), export_TextColor (re-inject color), destructor.
Replaced by: one co_* call, stack buffer, single Ragel pass.
Dead code removal:
- mux_string::UpperCase() — 0 remaining callers, 44 lines
- mux_string::LowerCase() — 0 remaining callers, 44 lines
- mux_string::UpperCaseFirst() — 0 remaining callers, 44 lines
592 smoke tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:21:10 -06:00
|
|
|
|
{
|
|
|
|
|
|
size_t nPlain = strlen(reinterpret_cast<const char *>(pName));
|
2026-03-23 21:52:18 -06:00
|
|
|
|
LBuf tmp = LBuf_Src("ok_name");
|
Integrate Ragel co_* into libmux.so; replace mux_string case mapping
Build integration:
- color_ops.rl and unicode_tables_c.h added to mux/lib/ and mux/include/
- Makefile.am: C compilation rule for Ragel-generated color_ops.c,
links into libmux.so alongside existing C++ objects
- Tables shared: co_* functions reference the existing DFA tables from
utf8tables.cpp via extern declarations (no duplication)
Function replacements:
- fun_lcstr: mux_string->LowerCase() → co_tolower() single-pass
- fun_ucstr: mux_string->UpperCase() → co_toupper() single-pass
- fun_capstr: mux_string->UpperCaseFirst() → co_totitle() single-pass
- predicates.cpp: LowerCase() for @addcommand → co_tolower()
- mail.cpp: UpperCase() for folder names → co_toupper() (2 sites)
Each replacement eliminates: heap allocation, mux_string import
(strip color → m_vcs vector), per-code-point DFA walk with
replace_Chars(), export_TextColor (re-inject color), destructor.
Replaced by: one co_* call, stack buffer, single Ragel pass.
Dead code removal:
- mux_string::UpperCase() — 0 remaining callers, 44 lines
- mux_string::LowerCase() — 0 remaining callers, 44 lines
- mux_string::UpperCaseFirst() — 0 remaining callers, 44 lines
592 smoke tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:21:10 -06:00
|
|
|
|
nPlain = co_tolower(tmp, pName, nPlain);
|
|
|
|
|
|
memcpy(pName, tmp, nPlain + 1);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-03-13 10:38:33 +00:00
|
|
|
|
if ( 0 == nargs
|
|
|
|
|
|
|| '\0' == pName[0]
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| ( pName[0] == '_'
|
|
|
|
|
|
&& pName[1] == '_'))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("That is not a valid command name."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validate object/attribute.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
if ( !parse_attrib(player, command, &thing, &pattr)
|
|
|
|
|
|
|| !pattr)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("No such attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!See_attr(player, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:39:35 -07:00
|
|
|
|
size_t nName = strlen(reinterpret_cast<char *>(pName));
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
auto it_old = mudstate.command_htab.find(std::vector<UTF8>(pName, pName + nName));
|
|
|
|
|
|
CMDENT *old = (it_old != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_old->second) : nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
CMDENT *cmd;
|
|
|
|
|
|
|
|
|
|
|
|
if ( old
|
|
|
|
|
|
&& (old->callseq & CS_ADDED))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Don't allow the same (thing,atr) in the list.
|
|
|
|
|
|
//
|
2026-03-19 08:48:37 -06:00
|
|
|
|
for (auto &entry : *old->addent)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
if ( entry.thing == thing
|
|
|
|
|
|
&& entry.atr == pattr->number)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(player, tprintf(M_("%s already added."), pName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Otherwise, add another (thing,atr) to the list.
|
|
|
|
|
|
//
|
2026-03-19 08:48:37 -06:00
|
|
|
|
old->addent->push_back(ADDENT{});
|
|
|
|
|
|
ADDENT &add = old->addent->back();
|
|
|
|
|
|
add.thing = thing;
|
|
|
|
|
|
add.atr = pattr->number;
|
|
|
|
|
|
add.name = reinterpret_cast<const char *>(pName);
|
2026-03-04 12:14:01 -07:00
|
|
|
|
if (bNoEval)
|
|
|
|
|
|
{
|
|
|
|
|
|
old->callseq |= CS_NOINTERP;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (old)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Delete the old built-in (which will later be added back as
|
|
|
|
|
|
// __name).
|
|
|
|
|
|
//
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(pName, pName + nName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
cmd = nullptr;
|
2012-03-15 06:33:45 -07:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
cmd = new CMDENT;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (...)
|
|
|
|
|
|
{
|
|
|
|
|
|
; // Nothing.
|
|
|
|
|
|
}
|
Remove ISOUTOFMEMORY macro; add per-site OOM recovery and fix g_dump_child_pid
Eliminate the ISOUTOFMEMORY macro that unconditionally aborted on allocation
failure. Each of the 23 call sites now handles OOM appropriately:
- Fatal sites (buffer pools, db array, anum table): mux_assert or OutOfMemory
- Recoverable sites (queue, mail, commands, guests, vattrs, config, restart,
forward lists): log the failure and return gracefully
Also fix g_dump_child_pid portability: volatile pid_t -> volatile sig_atomic_t
with explicit casts in ganl_adapter.cpp.
Close integer overflow issue as false alarm (getstring_noalloc uses a bounded
static buffer, so nBuffer+1 cannot wrap).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:31:20 -06:00
|
|
|
|
if (nullptr == cmd)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, OUT_OF_MEMORY);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
cmd->cmdname = StringClone(pName);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
cmd->switches = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
cmd->perms = 0;
|
|
|
|
|
|
cmd->extra = 0;
|
|
|
|
|
|
if ( old
|
|
|
|
|
|
&& (old->callseq & CS_LEADIN))
|
|
|
|
|
|
{
|
|
|
|
|
|
cmd->callseq = CS_ADDED|CS_ONE_ARG|CS_LEADIN;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cmd->callseq = CS_ADDED|CS_ONE_ARG;
|
|
|
|
|
|
}
|
2026-03-04 12:14:01 -07:00
|
|
|
|
if (bNoEval)
|
|
|
|
|
|
{
|
|
|
|
|
|
cmd->callseq |= CS_NOINTERP;
|
|
|
|
|
|
}
|
2012-03-15 08:20:40 -07:00
|
|
|
|
cmd->flags = CEF_ALLOC;
|
2026-03-19 08:48:37 -06:00
|
|
|
|
cmd->addent = new std::vector<ADDENT>();
|
|
|
|
|
|
cmd->addent->push_back(ADDENT{});
|
|
|
|
|
|
ADDENT &add = cmd->addent->back();
|
|
|
|
|
|
add.thing = thing;
|
|
|
|
|
|
add.atr = pattr->number;
|
|
|
|
|
|
add.name = reinterpret_cast<const char *>(pName);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pName, pName + nName), cmd);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( old
|
2026-03-03 18:39:35 -07:00
|
|
|
|
&& strcmp(reinterpret_cast<char *>(pName), reinterpret_cast<char *>(old->cmdname)) == 0)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// We are @addcommand'ing over a built-in command by its
|
|
|
|
|
|
// unaliased name, therefore, we want to re-target all the
|
|
|
|
|
|
// aliases.
|
|
|
|
|
|
//
|
2008-12-27 18:37:01 -08:00
|
|
|
|
UTF8 *p = tprintf(T("__%s"), pName);
|
2026-03-03 18:39:35 -07:00
|
|
|
|
size_t nP = strlen(reinterpret_cast<char *>(p));
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(p, p + nP));
|
|
|
|
|
|
for (auto &[k, v] : mudstate.command_htab) { if (v == old) v = cmd; }
|
|
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(p, p + nP), old);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We reset the one letter commands here so you can overload them.
|
|
|
|
|
|
//
|
2007-12-09 00:53:07 +00:00
|
|
|
|
cache_prefix_cmds();
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(player, tprintf(M_("Command %s added."), pName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-06 06:17:46 +00:00
|
|
|
|
void do_listcommands(dbref player, dbref caller, dbref enactor, int eval,
|
2007-09-02 13:48:10 -07:00
|
|
|
|
int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
CMDENT *old;
|
|
|
|
|
|
bool didit = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Let's make this case insensitive...
|
|
|
|
|
|
//
|
2007-03-25 17:01:06 +00:00
|
|
|
|
size_t nCased;
|
|
|
|
|
|
UTF8 *pCased = mux_strlwr(name, nCased);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-03-25 17:01:06 +00:00
|
|
|
|
if (*pCased)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
{
|
|
|
|
|
|
auto it = mudstate.command_htab.find(std::vector<UTF8>(pCased, pCased + nCased));
|
|
|
|
|
|
old = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( old
|
|
|
|
|
|
&& (old->callseq & CS_ADDED))
|
|
|
|
|
|
{
|
|
|
|
|
|
// If it's already found in the hash table, and it's being added
|
|
|
|
|
|
// using the same object and attribute...
|
|
|
|
|
|
//
|
2026-03-19 08:48:37 -06:00
|
|
|
|
for (auto &entry : *old->addent)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
ATTR *ap = reinterpret_cast<ATTR *>(atr_num(entry.atr));
|
2007-03-14 22:33:04 +00:00
|
|
|
|
const UTF8 *pName = T("(WARNING: Bad Attribute Number)");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
pName = ap->name;
|
|
|
|
|
|
}
|
2026-03-19 08:48:37 -06:00
|
|
|
|
notify(player, tprintf(T("%s: #%d/%s"), reinterpret_cast<const UTF8 *>(entry.name.c_str()), entry.thing, pName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(player, tprintf(M_("%s not found in command table."), pCased));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
for (auto &[key, val] : mudstate.command_htab)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
old = static_cast<CMDENT*>(val);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (old->callseq & CS_ADDED)
|
|
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
const UTF8 *pKeyName = key.data();
|
|
|
|
|
|
int nKeyName = static_cast<int>(key.size());
|
2026-03-19 08:48:37 -06:00
|
|
|
|
for (auto &entry : *old->addent)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
if ( static_cast<size_t>(nKeyName) != entry.name.size()
|
|
|
|
|
|
|| memcmp(pKeyName, entry.name.c_str(), nKeyName) != 0)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-03-19 08:48:37 -06:00
|
|
|
|
ATTR *ap = reinterpret_cast<ATTR *>(atr_num(entry.atr));
|
2007-03-14 22:33:04 +00:00
|
|
|
|
const UTF8 *pName = T("(WARNING: Bad Attribute Number)");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
pName = ap->name;
|
|
|
|
|
|
}
|
2026-03-19 08:48:37 -06:00
|
|
|
|
notify(player, tprintf(T("%s: #%d/%s"), reinterpret_cast<const UTF8 *>(entry.name.c_str()),
|
|
|
|
|
|
entry.thing, pName));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
didit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2007-01-06 22:53:01 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!didit)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("No added commands found in command table."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_delcommand
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
2007-09-01 23:47:27 -07:00
|
|
|
|
int eval,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *command,
|
|
|
|
|
|
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);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (!*name)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Sorry."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref thing = NOTHING;
|
|
|
|
|
|
int atr = NOTHING;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
if (*command)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !parse_attrib(player, command, &thing, &pattr)
|
|
|
|
|
|
|| !pattr)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("No such attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!See_attr(player, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
atr = pattr->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Let's make this case insensitive...
|
|
|
|
|
|
//
|
2007-03-25 17:01:06 +00:00
|
|
|
|
size_t nCased;
|
|
|
|
|
|
UTF8 *pCased = mux_strlwr(name, nCased);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
CMDENT *old, *cmd;
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
{
|
|
|
|
|
|
auto it = mudstate.command_htab.find(std::vector<UTF8>(pCased, pCased + nCased));
|
|
|
|
|
|
old = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( old
|
|
|
|
|
|
&& (old->callseq & CS_ADDED))
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
UTF8 *p__Name = tprintf(T("__%s"), pCased);
|
2026-03-03 18:39:35 -07:00
|
|
|
|
size_t n__Name = strlen(reinterpret_cast<char *>(p__Name));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (command[0] == '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
// Delete all @addcommand'ed associations with the given name.
|
|
|
|
|
|
//
|
2026-03-19 08:48:37 -06:00
|
|
|
|
delete old->addent;
|
|
|
|
|
|
old->addent = nullptr;
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(pCased, pCased + nCased));
|
|
|
|
|
|
{
|
|
|
|
|
|
auto it_cmd = mudstate.command_htab.find(std::vector<UTF8>(p__Name, p__Name + n__Name));
|
|
|
|
|
|
cmd = (it_cmd != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_cmd->second) : nullptr;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (cmd)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
size_t nCmdName = strlen(reinterpret_cast<char *>(cmd->cmdname));
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(cmd->cmdname, cmd->cmdname + nCmdName), cmd);
|
2026-03-03 18:39:35 -07:00
|
|
|
|
if (strcmp(reinterpret_cast<char *>(pCased), reinterpret_cast<char *>(cmd->cmdname)) != 0)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pCased, pCased + nCased), cmd);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(p__Name, p__Name + n__Name));
|
|
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(p__Name, p__Name + n__Name), cmd);
|
|
|
|
|
|
for (auto &[k, v] : mudstate.command_htab) { if (v == old) v = cmd; }
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-24 14:46:49 -06:00
|
|
|
|
// No backup command to restore. Remove all hash
|
|
|
|
|
|
// entries that still reference 'old'.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2026-03-24 14:46:49 -06:00
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(p__Name, p__Name + n__Name));
|
|
|
|
|
|
for (auto it2 = mudstate.command_htab.begin(); it2 != mudstate.command_htab.end(); )
|
|
|
|
|
|
{
|
|
|
|
|
|
if (it2->second == old)
|
|
|
|
|
|
{
|
|
|
|
|
|
it2 = mudstate.command_htab.erase(it2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
++it2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
MEMFREE(old->cmdname);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
old->cmdname = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
MEMFREE(old);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
old = nullptr;
|
2007-12-09 00:53:07 +00:00
|
|
|
|
cache_prefix_cmds();
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Done."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove only the (name,thing,atr) association.
|
|
|
|
|
|
//
|
2026-03-19 08:48:37 -06:00
|
|
|
|
for (auto it = old->addent->begin(); it != old->addent->end(); ++it)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
if ( it->thing == thing
|
|
|
|
|
|
&& it->atr == atr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
old->addent->erase(it);
|
|
|
|
|
|
if (old->addent->empty())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
delete old->addent;
|
|
|
|
|
|
old->addent = nullptr;
|
|
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(pCased, pCased + nCased));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
auto it_cmd2 = mudstate.command_htab.find(std::vector<UTF8>(p__Name, p__Name + n__Name));
|
|
|
|
|
|
cmd = (it_cmd2 != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it_cmd2->second) : nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cmd)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t nCmdName = strlen(reinterpret_cast<char *>(cmd->cmdname));
|
|
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(cmd->cmdname, cmd->cmdname + nCmdName),
|
|
|
|
|
|
cmd);
|
|
|
|
|
|
if (strcmp(reinterpret_cast<char *>(pCased), reinterpret_cast<char *>(cmd->cmdname)) != 0)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-19 08:48:37 -06:00
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pCased, pCased + nCased),
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
cmd);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-19 08:48:37 -06:00
|
|
|
|
|
|
|
|
|
|
mudstate.command_htab.erase(std::vector<UTF8>(p__Name, p__Name + n__Name));
|
|
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(p__Name, p__Name + n__Name),
|
|
|
|
|
|
cmd);
|
|
|
|
|
|
for (auto &[k, v] : mudstate.command_htab) { if (v == old) v = cmd; }
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-19 08:48:37 -06:00
|
|
|
|
MEMFREE(old->cmdname);
|
|
|
|
|
|
old->cmdname = nullptr;
|
|
|
|
|
|
MEMFREE(old);
|
|
|
|
|
|
old = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-12-09 00:53:07 +00:00
|
|
|
|
cache_prefix_cmds();
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Done."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Command not found in command table."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Command not found in command table."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_quitprog(dbref player, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref doer;
|
|
|
|
|
|
|
|
|
|
|
|
if (*name)
|
|
|
|
|
|
{
|
|
|
|
|
|
doer = match_thing(player, name);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
doer = player;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( !( Prog(player)
|
|
|
|
|
|
|| Prog(Owner(player)))
|
|
|
|
|
|
&& player != doer)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !Good_obj(doer)
|
|
|
|
|
|
|| !isPlayer(doer))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("That is not a player."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Connected(doer))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("That player is not connected."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
if (!player_has_program(doer))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Player is not in an @program."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
program_data* program = detach_player_program(doer);
|
|
|
|
|
|
if (program)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
for (auto& wait_reg : program->wait_regs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
if (wait_reg)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
RegRelease(wait_reg);
|
|
|
|
|
|
wait_reg = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
NamedRegsClear(program->named_wait_regs);
|
2006-11-25 08:07:09 +00:00
|
|
|
|
MEMFREE(program);
|
2022-03-15 19:47:08 -06:00
|
|
|
|
program = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
atr_clr(doer, A_PROGCMD);
|
2026-07-27 12:39:20 +00:00
|
|
|
|
notify(player, M_("@program cleared."));
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(doer, M_("Your @program has been terminated."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_prog
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
2007-09-01 23:47:27 -07:00
|
|
|
|
int eval,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *command,
|
|
|
|
|
|
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);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( !name
|
|
|
|
|
|
|| !*name)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("No players specified."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 19:47:08 -06:00
|
|
|
|
const dbref doer = match_thing(player, name);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( !( Prog(player)
|
|
|
|
|
|
|| Prog(Owner(player)))
|
|
|
|
|
|
&& player != doer)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !Good_obj(doer)
|
|
|
|
|
|
|| !isPlayer(doer))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("That is not a player."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Connected(doer))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("That player is not connected."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2010-07-23 20:46:16 -07:00
|
|
|
|
|
|
|
|
|
|
// Check to see if the enactor already has an @prog input pending.
|
|
|
|
|
|
//
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
if (player_has_program(doer))
|
2010-07-23 20:46:16 -07:00
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Input already pending."));
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
return;
|
2010-07-23 20:46:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *msg = command;
|
2022-03-15 19:47:08 -06:00
|
|
|
|
const UTF8 *attrib = parse_to(&msg, ':', 1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (msg && *msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(doer, msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
if (!parse_attrib(player, attrib, &thing, &ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOMATCH_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
int lev;
|
|
|
|
|
|
dbref parent;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *pBuffer = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool bFound = false;
|
|
|
|
|
|
ITER_PARENTS(thing, parent, lev)
|
|
|
|
|
|
{
|
2007-03-07 05:14:20 +00:00
|
|
|
|
pBuffer = atr_get("do_prog.1405", parent, ap->number, &aowner, &aflags);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (pBuffer[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
bFound = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(pBuffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (bFound)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ( God(player)
|
|
|
|
|
|
|| !God(thing))
|
|
|
|
|
|
&& See_attr(player, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_add_raw(doer, A_PROGCMD, pBuffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
free_lbuf(pBuffer);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(pBuffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("Attribute not present on object."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(player, M_("No such attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 19:47:08 -06:00
|
|
|
|
const auto program = static_cast<program_data*>(MEMALLOC(sizeof(program_data)));
|
Remove ISOUTOFMEMORY macro; add per-site OOM recovery and fix g_dump_child_pid
Eliminate the ISOUTOFMEMORY macro that unconditionally aborted on allocation
failure. Each of the 23 call sites now handles OOM appropriately:
- Fatal sites (buffer pools, db array, anum table): mux_assert or OutOfMemory
- Recoverable sites (queue, mail, commands, guests, vattrs, config, restart,
forward lists): log the failure and return gracefully
Also fix g_dump_child_pid portability: volatile pid_t -> volatile sig_atomic_t
with explicit casts in ganl_adapter.cpp.
Close integer overflow issue as false alarm (getstring_noalloc uses a bounded
static buffer, so nBuffer+1 cannot wrap).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:31:20 -06:00
|
|
|
|
if (nullptr == program)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, OUT_OF_MEMORY);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
program->wait_enactor = player;
|
|
|
|
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
program->wait_regs[i] = mudstate.global_regs[i];
|
2006-11-08 05:46:45 +00:00
|
|
|
|
if (mudstate.global_regs[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
RegAddRef(mudstate.global_regs[i]);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
program->named_wait_regs = NamedRegsCopy(mudstate.named_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Now, start waiting.
|
|
|
|
|
|
//
|
Wrap descriptor access in timer, file_c, and predicates
Add 6 new connection accessors (for_each_player_desc, send_keepalive_nops,
player_has_program, detach_player_program, set_player_program,
send_prog_prompt) and convert all remaining engine files to use them.
Stage 2 complete: all engine files now have zero direct descriptor
collection access. Only driver/boundary files (netcommon, bsd,
ganl_adapter, sitemon, db) retain direct access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:02:34 -06:00
|
|
|
|
set_player_program(doer, program);
|
|
|
|
|
|
send_prog_prompt(doer);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* do_restart: Restarts the game.
|
|
|
|
|
|
*/
|
2007-09-02 11:03:02 -07:00
|
|
|
|
void do_restart(dbref executor, dbref caller, dbref enactor, int eval, int key)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-02 11:03:02 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
|
|
|
|
|
|
if (!Can_SiteAdmin(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bDenied = false;
|
2007-12-26 08:17:12 +00:00
|
|
|
|
#if defined(HAVE_WORKING_FORK)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (mudstate.dumping)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(executor, M_("Dumping. Please try again later."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bDenied = true;
|
|
|
|
|
|
}
|
2007-12-26 08:17:12 +00:00
|
|
|
|
#endif // HAVE_WORKING_FORK
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!mudstate.bCanRestart)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(executor, M_("Server just started. Please try again in a few seconds."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bDenied = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (bDenied)
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T("Restart requested but not executed by "));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
log_name(executor);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-03-18 10:59:57 -07:00
|
|
|
|
#ifdef UNIX_SSL
|
2026-07-28 12:07:45 -06:00
|
|
|
|
raw_broadcast(0, M_("GAME: Restart by %s, please wait. (All SSL connections will be dropped.)"), Moniker(Owner(executor)));
|
2007-03-22 04:03:41 +00:00
|
|
|
|
#else
|
2026-07-28 12:07:45 -06:00
|
|
|
|
raw_broadcast(0, M_("GAME: Restart by %s, please wait."), Moniker(Owner(executor)));
|
2007-03-22 04:03:41 +00:00
|
|
|
|
#endif
|
2006-09-01 22:59:23 +00:00
|
|
|
|
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T("Restart by "));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
log_name(executor);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
|
fix(net): decline the restart when restart.db cannot be written (#2043)
#2042 made a failed dump visible; it could not make it survivable, because
by the time dump_restart_db() ran the game was already dismantled. This
moves the dump ahead of the teardown so the restart can simply not happen.
Before, do_restart() ran prepare_for_restart(), final_modules(), pcache_sync,
dump_database_internal, SYNC and CLOSE, and only then wrote restart.db. A
failed write was therefore discovered with nothing left to return to, and the
successor exec'd into a game it could not serve: prepare_for_restart()
detaches the listener fds but deliberately leaves them OPEN for the successor
to adopt, so without restart.db the successor collides with its own
inheritance --
[Kqueue:7] Listener detached (fd left open).
[Kqueue:6] bind() failed: Address already in use
-- and exits. @restart on a full disk made the game disappear.
The ordering that makes declining possible: listener detachment is step 6 of
prepare_for_restart(), not step 1. Steps 1 (record listeners) and 2 (close
TLS/WebSocket) are split into their own methods so DumpRestartDb() can run
exactly what the dump needs, leaving the engine up and the listeners attached
when the result is known. prepare_for_restart() still calls both, so there is
one implementation and any other caller is unaffected; both are idempotent so
re-running them costs nothing.
Step 2 must precede the dump, not follow it: a TLS descriptor written into
restart.db is restored by a successor with no TLS state for it (#2032). That
is also why declining is not free, and the messages say so rather than
implying nothing happened -- those sessions are already gone.
This is abstention, not recovery. No retry, no fd reclamation, no heuristic
about whether the failure looked transient. The dump either produced a
complete file or it did not, and the restart proceeds or does not.
Plumbing: dump_restart_db() returns bool through externs.h, the engine shim
in conn_bridge.cpp, and CDriverControl::DumpRestartDb() -- which already
returned MUX_RESULT and always said MUX_S_OK. No interface method was added,
so the module ABI is unchanged. A missing driver control now reads as "cannot
dump" rather than silent success; proceeding would be the exact failure this
reports.
Verified live on macOS/arm64, past the 15s bCanRestart gate:
normal @restart 2 game logs, 725 bytes answered, no restart.db.tmp
dump fails 1 game log, 725 bytes answered, game ALIVE
RST/DUMP : Could not write restart.db (write failed)...
WIZ/RSTRT : Restart CANCELLED ... Requested by Wizard(#1)
neither restart.db nor restart.db.tmp published
Before this change that second row was ConnectionRefusedError and a dead
process.
The new broadcast is an M_() string, so mux/po/tinymux.pot is regenerated and
es/ko/xx merged; xx.po is a complete-policy pseudo-locale and gets its "[xx] "
entry. The catalogue guard caught the omission -- test-nls failed with
"marked in source but absent from .pot" and named the fix.
make test 35 passed / 1 skipped / 0 failed
(jit=yes stubslave=no nls=yes realitylvls=yes wodrealms=yes).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 09:57:42 -06:00
|
|
|
|
// Write restart.db BEFORE the teardown, and decline the restart if it
|
|
|
|
|
|
// cannot be written (#2043).
|
|
|
|
|
|
//
|
|
|
|
|
|
// This used to happen after prepare_for_restart(), final_modules() and
|
|
|
|
|
|
// CLOSE, i.e. after the point of no return -- so a failed write was
|
|
|
|
|
|
// discovered when there was nothing left to return to, and the successor
|
|
|
|
|
|
// exec'd into a game it could not serve: prepare_for_restart() detaches
|
|
|
|
|
|
// the listener fds but leaves them open for the successor to adopt, so
|
|
|
|
|
|
// without restart.db the successor collides with its own inheritance and
|
|
|
|
|
|
// dies on "bind() failed: Address already in use".
|
|
|
|
|
|
//
|
|
|
|
|
|
// DumpRestartDb() records the listeners and closes the TLS/WebSocket
|
|
|
|
|
|
// sessions itself, because the dump needs both done first. Those are the
|
|
|
|
|
|
// only destructive steps that precede the decision, which is why the
|
|
|
|
|
|
// decline below is not free and says so.
|
|
|
|
|
|
//
|
|
|
|
|
|
#if defined(HAVE_WORKING_FORK)
|
|
|
|
|
|
if (!dump_restart_db())
|
|
|
|
|
|
{
|
|
|
|
|
|
raw_broadcast(0, M_("GAME: Restart could not be prepared and has been cancelled. The game is still running."));
|
|
|
|
|
|
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
|
|
|
|
|
|
log_text(T("Restart CANCELLED: restart.db could not be written. Requested by "));
|
|
|
|
|
|
log_name(executor);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // HAVE_WORKING_FORK
|
|
|
|
|
|
|
2026-03-02 22:15:00 -07:00
|
|
|
|
g_GanlAdapter.prepare_for_restart();
|
2007-03-22 23:17:01 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
local_presync_database();
|
2007-09-01 16:44:47 -07:00
|
|
|
|
ServerEventsSinkNode *p = g_pServerEventsSinkListHead;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
while (nullptr != p)
|
2007-09-01 16:44:47 -07:00
|
|
|
|
{
|
|
|
|
|
|
p->pSink->presync_database();
|
|
|
|
|
|
p = p->pNext;
|
|
|
|
|
|
}
|
2016-08-21 19:46:21 +00:00
|
|
|
|
#if defined(STUB_SLAVE)
|
|
|
|
|
|
final_stubslave();
|
|
|
|
|
|
#endif // STUB_SLAVE
|
2007-09-23 23:25:10 -07:00
|
|
|
|
final_modules();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
pcache_sync();
|
|
|
|
|
|
dump_database_internal(DUMP_I_RESTART);
|
|
|
|
|
|
SYNC;
|
|
|
|
|
|
CLOSE;
|
|
|
|
|
|
|
2008-01-29 12:16:54 -08:00
|
|
|
|
#if defined(WINDOWS_PROCESSES)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
exit(12345678);
|
2008-01-29 12:16:54 -08:00
|
|
|
|
#elif defined(UNIX_PROCESSES)
|
fix(net): decline the restart when restart.db cannot be written (#2043)
#2042 made a failed dump visible; it could not make it survivable, because
by the time dump_restart_db() ran the game was already dismantled. This
moves the dump ahead of the teardown so the restart can simply not happen.
Before, do_restart() ran prepare_for_restart(), final_modules(), pcache_sync,
dump_database_internal, SYNC and CLOSE, and only then wrote restart.db. A
failed write was therefore discovered with nothing left to return to, and the
successor exec'd into a game it could not serve: prepare_for_restart()
detaches the listener fds but deliberately leaves them OPEN for the successor
to adopt, so without restart.db the successor collides with its own
inheritance --
[Kqueue:7] Listener detached (fd left open).
[Kqueue:6] bind() failed: Address already in use
-- and exits. @restart on a full disk made the game disappear.
The ordering that makes declining possible: listener detachment is step 6 of
prepare_for_restart(), not step 1. Steps 1 (record listeners) and 2 (close
TLS/WebSocket) are split into their own methods so DumpRestartDb() can run
exactly what the dump needs, leaving the engine up and the listeners attached
when the result is known. prepare_for_restart() still calls both, so there is
one implementation and any other caller is unaffected; both are idempotent so
re-running them costs nothing.
Step 2 must precede the dump, not follow it: a TLS descriptor written into
restart.db is restored by a successor with no TLS state for it (#2032). That
is also why declining is not free, and the messages say so rather than
implying nothing happened -- those sessions are already gone.
This is abstention, not recovery. No retry, no fd reclamation, no heuristic
about whether the failure looked transient. The dump either produced a
complete file or it did not, and the restart proceeds or does not.
Plumbing: dump_restart_db() returns bool through externs.h, the engine shim
in conn_bridge.cpp, and CDriverControl::DumpRestartDb() -- which already
returned MUX_RESULT and always said MUX_S_OK. No interface method was added,
so the module ABI is unchanged. A missing driver control now reads as "cannot
dump" rather than silent success; proceeding would be the exact failure this
reports.
Verified live on macOS/arm64, past the 15s bCanRestart gate:
normal @restart 2 game logs, 725 bytes answered, no restart.db.tmp
dump fails 1 game log, 725 bytes answered, game ALIVE
RST/DUMP : Could not write restart.db (write failed)...
WIZ/RSTRT : Restart CANCELLED ... Requested by Wizard(#1)
neither restart.db nor restart.db.tmp published
Before this change that second row was ConnectionRefusedError and a dead
process.
The new broadcast is an M_() string, so mux/po/tinymux.pot is regenerated and
es/ko/xx merged; xx.po is a complete-policy pseudo-locale and gets its "[xx] "
entry. The catalogue guard caught the omission -- test-nls failed with
"marked in source but absent from .pot" and named the fix.
make test 35 passed / 1 skipped / 0 failed
(jit=yes stubslave=no nls=yes realitylvls=yes wodrealms=yes).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 09:57:42 -06:00
|
|
|
|
// restart.db was written above, before the teardown, and the restart was
|
|
|
|
|
|
// declined if it could not be (#2043). Dumping here as well would
|
|
|
|
|
|
// re-serialise descriptors the teardown has since closed.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
Log.StopLogging();
|
|
|
|
|
|
|
fix(restart): preserve the -p and -e command-line values across @restart (#2199)
do_restart() rebuilds its exec argv from mudconf.pid_file and
mudconf.log_dir. Both were read in three places and written in NONE:
predicates.cpp the execl <- read
engine_com.cpp GetConfig -> DRIVER_CONFIG.pid_file <- read
engine_com.cpp GetConfig -> DRIVER_CONFIG.log_dir <- read
There is no `pid_file` or `log_dir` config directive either, so nothing in
netmux.conf could populate them. Both are driver-owned CLI values --
driver.cpp says so outright ("pid_file is driver-owned -- set from CLI or
default") -- and the engine had no way to learn them. So every @restart on
every site exec'd with an empty -p and -e.
Impact is quiet, which is why it lasted: a site running
netmux -p /var/run/mux/netmux.pid
has its pidfile revert to the default netmux.pid in the working directory
after the first restart, and stops maintaining the configured path. The
pid inside the stale file stays correct, because execve preserves it, so
nothing breaks until something else moves. Same for -e.
The fingerprint is visible in ps on any restarted 2.14: `-p` with nothing
after it.
Fix follows #817's precedent exactly -- driver-owned strings the engine
needs, handed over at bridge init:
- g_driver_pid_file / g_driver_log_dir replace two file-static CLI
variables in driver.cpp (pErrorBasename was also a poor name for
something that is a log directory). Globals for the same reason
g_version is one: the CDriverControl bridge is a different TU.
- mux_IDriverControl gains GetInvocationPaths. IID bumped D4D6 -> D4D7;
the comment now records both bumps and why a vtable change needs one.
- conn_bridge_init caches them into mudconf. Safe there because
Startup() runs after LoadGame(), so cf_init() cannot wipe them
afterwards. Pointers are stored, not copied: they are driver-owned
storage that lives for the process, and nothing in the engine frees or
reassigns either field.
- CScriptDriverControl implements it too, returning empty strings.
muxscript writes no pidfile and has no @restart; an invented default
would only be wrong somewhere later.
The argv is now built dynamically and omits an option it has no value for.
-c, -p and -e are all CLI_REQUIRED, so a bare flag makes the successor log
"Option 'x' requires an argument, but none was found." Populating the
fields alone would have left that warning for any site that supplies only
one of the two -- which is what test-scenario showed, since run.sh passes
-p and not -e.
Verified on a live restart, reading the successor's argv (execve replaces
argv and preserves the pid, so this is direct evidence):
boot: ./bin/netmux -c netmux.conf -p custom-name.pid -e logs
restart: netmux -c netmux.conf -p custom-name.pid -e logs
ok - successor kept -p custom-name.pid
ok - successor kept -e logs
ok - no missing-argument warning in the log
Catch-verified by removing only the bridge caching:
restart: netmux -c netmux.conf -p
not ok - successor lost the -p value
not ok - successor lost the -e value
not ok - successor logged a missing-argument warning:
15:Warning: Option '-p' requires an argument, but none was found.
tests/scenario/restart_helpers.py gains the assertion; it already pays for
a restart, so this costs no extra cycle. It skips if the server under test
was not started with -p.
Clean rebuild, since the IDriverControl vtable changed.
make test: 36 targets, 34 passed, 2 skipped (NLS), 0 failed
config: jit=yes stubslave=yes nls=no realitylvls=yes wodrealms=yes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:39:34 -06:00
|
|
|
|
// #2199: build argv, omitting any option whose value is empty.
|
|
|
|
|
|
//
|
|
|
|
|
|
// -c, -p and -e are all CLI_REQUIRED, so passing a bare flag makes the
|
|
|
|
|
|
// successor log "Option 'x' requires an argument, but none was found."
|
|
|
|
|
|
// That is what a restarted server used to do for BOTH -p and -e, every
|
|
|
|
|
|
// time, because mudconf.pid_file and mudconf.log_dir were read here and
|
|
|
|
|
|
// written nowhere. They are populated now, but a site that supplies
|
|
|
|
|
|
// only one of the two would still collect a warning for the other on
|
|
|
|
|
|
// every restart -- so skip what we do not have rather than pass "".
|
|
|
|
|
|
//
|
|
|
|
|
|
const char *argvRestart[9];
|
|
|
|
|
|
int argcRestart = 0;
|
|
|
|
|
|
argvRestart[argcRestart++] = "netmux";
|
|
|
|
|
|
argvRestart[argcRestart++] = "-c";
|
|
|
|
|
|
argvRestart[argcRestart++] =
|
|
|
|
|
|
reinterpret_cast<const char *>(mudconf.config_file);
|
|
|
|
|
|
if ( nullptr != mudconf.pid_file
|
|
|
|
|
|
&& '\0' != mudconf.pid_file[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
argvRestart[argcRestart++] = "-p";
|
|
|
|
|
|
argvRestart[argcRestart++] =
|
|
|
|
|
|
reinterpret_cast<const char *>(mudconf.pid_file);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( nullptr != mudconf.log_dir
|
|
|
|
|
|
&& '\0' != mudconf.log_dir[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
argvRestart[argcRestart++] = "-e";
|
|
|
|
|
|
argvRestart[argcRestart++] =
|
|
|
|
|
|
reinterpret_cast<const char *>(mudconf.log_dir);
|
|
|
|
|
|
}
|
|
|
|
|
|
argvRestart[argcRestart] = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
execv("bin/netmux", const_cast<char *const *>(argvRestart));
|
2009-05-13 20:46:56 -07:00
|
|
|
|
mux_assert(false);
|
2008-01-29 12:16:54 -08:00
|
|
|
|
#endif // UNIX_PROCESSES
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* do_comment: Implement the @@ (comment) command. Very cpu-intensive :-)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-09-02 11:03:02 -07:00
|
|
|
|
void do_comment(dbref executor, dbref caller, dbref enactor, int eval, int key)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-28 17:05:42 +00:00
|
|
|
|
UNUSED_PARAMETER(executor);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-02 11:03:02 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_eval(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *arg1, const UTF8 *cargs[], int ncargs)
|
2007-03-28 17:05:42 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(executor);
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(eval);
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(arg1);
|
|
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2007-03-28 17:05:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
static dbref promote_dflt(dbref old, dbref new0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( old == NOPERM
|
|
|
|
|
|
|| new0 == NOPERM)
|
|
|
|
|
|
{
|
|
|
|
|
|
return NOPERM;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( old == AMBIGUOUS
|
|
|
|
|
|
|| new0 == AMBIGUOUS)
|
|
|
|
|
|
{
|
|
|
|
|
|
return AMBIGUOUS;
|
|
|
|
|
|
}
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(#2136): flip fargs to const UTF8 * const — and convert every site the compiler surfaced
The flip: FUNCTION/XFUNCTION/FUN::fun/delim_check and the module
interfaces take `const UTF8 * const fargs[]`. Double-const is
load-bearing: C++ qualification conversion needs const at both pointer
levels, so builder-side `UTF8 *[]` arrays convert implicitly — the
evaluator, the JIT marshaller, and every owner site need zero casts,
and slot reassignment inside bodies becomes a compile error for free.
The conversions: the flip landed first so the compiler enumerated every
violation; this commit is that inventory worked to zero — ~250 sites
across funceval, funceval2, functions, funmath, help, mail, session,
powers, levels, predicates, conf, walkdb, stringutil, timeutil/
date_scan (regenerated, one-line diff), exp3, and mux_main, each
classified per docs/campaign-2136-const-fargs.md's four recipes.
New idioms (functions.h): trim_space_sep_n() — non-destructive trim for
(pointer, length) consumers, so trim-then-scan sites need no copy at
all; FargVec — the argv counterpart of FargCopy for CS_ARGV handlers.
countwords() and DecodeListOfIntegers() rewritten non-destructive.
The flip deleted more than it added: #2157's fun_munge list1 copy, the
engine_com help-topic copy, fun_index's in-place NUL write, and five
const_casts (process_sex x4, sha1_helper). const_cast budget: zero
added.
Trap recorded in the brief: an old-signature definition doesn't fail
the build — it becomes a C++ overload, and the new-signature symbol
stays undefined until dlopen(RTLD_NOW). delim_check, the conn_bridge
bridges, the dbt_spike stub, and exp3::Call were all silently shadowed;
muxscript was the only host that noticed, because netmux's own net.cpp
resolved the flat-namespace lookup. After any signature flip, grep the
old spelling.
Verified: make test EXPECT_CONFIG="jit=yes" (35 passed / 0 failed) and
make test-scenario, including the new tests/scenario/sidefx_fargs.py
that live-probes the class-3 wrappers smoke never touches (pemit/
trigger/link/tel/wipe/destroy).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 14:34:23 -06:00
|
|
|
|
dbref match_possessed(dbref player, dbref thing, const UTF8 *target, dbref dflt, bool check_enter)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// First, check normally.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (Good_obj(dflt))
|
|
|
|
|
|
{
|
|
|
|
|
|
return dflt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Didn't find it directly. Recursively do a contents check.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref result, result1;
|
feat(#2136): flip fargs to const UTF8 * const — and convert every site the compiler surfaced
The flip: FUNCTION/XFUNCTION/FUN::fun/delim_check and the module
interfaces take `const UTF8 * const fargs[]`. Double-const is
load-bearing: C++ qualification conversion needs const at both pointer
levels, so builder-side `UTF8 *[]` arrays convert implicitly — the
evaluator, the JIT marshaller, and every owner site need zero casts,
and slot reassignment inside bodies becomes a compile error for free.
The conversions: the flip landed first so the compiler enumerated every
violation; this commit is that inventory worked to zero — ~250 sites
across funceval, funceval2, functions, funmath, help, mail, session,
powers, levels, predicates, conf, walkdb, stringutil, timeutil/
date_scan (regenerated, one-line diff), exp3, and mux_main, each
classified per docs/campaign-2136-const-fargs.md's four recipes.
New idioms (functions.h): trim_space_sep_n() — non-destructive trim for
(pointer, length) consumers, so trim-then-scan sites need no copy at
all; FargVec — the argv counterpart of FargCopy for CS_ARGV handlers.
countwords() and DecodeListOfIntegers() rewritten non-destructive.
The flip deleted more than it added: #2157's fun_munge list1 copy, the
engine_com help-topic copy, fun_index's in-place NUL write, and five
const_casts (process_sex x4, sha1_helper). const_cast budget: zero
added.
Trap recorded in the brief: an old-signature definition doesn't fail
the build — it becomes a C++ overload, and the new-signature symbol
stays undefined until dlopen(RTLD_NOW). delim_check, the conn_bridge
bridges, the dbt_spike stub, and exp3::Call were all silently shadowed;
muxscript was the only host that noticed, because netmux's own net.cpp
resolved the flat-namespace lookup. After any signature flip, grep the
old spelling.
Verified: make test EXPECT_CONFIG="jit=yes" (35 passed / 0 failed) and
make test-scenario, including the new tests/scenario/sidefx_fargs.py
that live-probes the class-3 wrappers smoke never touches (pemit/
trigger/link/tel/wipe/destroy).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 14:34:23 -06:00
|
|
|
|
UTF8 *d1;
|
|
|
|
|
|
const UTF8 *place, *s1, *temp;
|
|
|
|
|
|
const UTF8 *start = target;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
while (*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Fail if no ' characters.
|
|
|
|
|
|
//
|
|
|
|
|
|
place = target;
|
feat(#2136): flip fargs to const UTF8 * const — and convert every site the compiler surfaced
The flip: FUNCTION/XFUNCTION/FUN::fun/delim_check and the module
interfaces take `const UTF8 * const fargs[]`. Double-const is
load-bearing: C++ qualification conversion needs const at both pointer
levels, so builder-side `UTF8 *[]` arrays convert implicitly — the
evaluator, the JIT marshaller, and every owner site need zero casts,
and slot reassignment inside bodies becomes a compile error for free.
The conversions: the flip landed first so the compiler enumerated every
violation; this commit is that inventory worked to zero — ~250 sites
across funceval, funceval2, functions, funmath, help, mail, session,
powers, levels, predicates, conf, walkdb, stringutil, timeutil/
date_scan (regenerated, one-line diff), exp3, and mux_main, each
classified per docs/campaign-2136-const-fargs.md's four recipes.
New idioms (functions.h): trim_space_sep_n() — non-destructive trim for
(pointer, length) consumers, so trim-then-scan sites need no copy at
all; FargVec — the argv counterpart of FargCopy for CS_ARGV handlers.
countwords() and DecodeListOfIntegers() rewritten non-destructive.
The flip deleted more than it added: #2157's fun_munge list1 copy, the
engine_com help-topic copy, fun_index's in-place NUL write, and five
const_casts (process_sex x4, sha1_helper). const_cast budget: zero
added.
Trap recorded in the brief: an old-signature definition doesn't fail
the build — it becomes a C++ overload, and the new-signature symbol
stays undefined until dlopen(RTLD_NOW). delim_check, the conn_bridge
bridges, the dbt_spike stub, and exp3::Call were all silently shadowed;
muxscript was the only host that noticed, because netmux's own net.cpp
resolved the flat-namespace lookup. After any signature flip, grep the
old spelling.
Verified: make test EXPECT_CONFIG="jit=yes" (35 passed / 0 failed) and
make test-scenario, including the new tests/scenario/sidefx_fargs.py
that live-probes the class-3 wrappers smoke never touches (pemit/
trigger/link/tel/wipe/destroy).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 14:34:23 -06:00
|
|
|
|
target = reinterpret_cast<const UTF8 *>(strchr(reinterpret_cast<const char *>(place), '\''));
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ( target == nullptr
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| !*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dflt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If string started with a ', skip past it
|
|
|
|
|
|
//
|
|
|
|
|
|
if (place == target)
|
|
|
|
|
|
{
|
|
|
|
|
|
target++;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If next character is not an s or a space, skip past
|
|
|
|
|
|
//
|
|
|
|
|
|
temp = target++;
|
|
|
|
|
|
if (!*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dflt;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( *target != 's'
|
|
|
|
|
|
&& *target != 'S'
|
|
|
|
|
|
&& *target != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If character was not a space make sure the following character is
|
|
|
|
|
|
// a space.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (*target != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
target++;
|
|
|
|
|
|
if (!*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dflt;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (*target != ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copy the container name to a new buffer so we can terminate it.
|
|
|
|
|
|
//
|
|
|
|
|
|
{
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf buff = LBuf_Src("is_posess");
|
|
|
|
|
|
d1 = buff.get();
|
|
|
|
|
|
for (s1 = start; *s1 && (s1 < temp); *d1++ = (*s1++))
|
|
|
|
|
|
{
|
|
|
|
|
|
; // Nothing.
|
|
|
|
|
|
}
|
|
|
|
|
|
*d1 = '\0';
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
// Look for the container here and in our inventory. Skip past if we
|
|
|
|
|
|
// can't find it.
|
|
|
|
|
|
//
|
|
|
|
|
|
init_match(thing, buff, NOTYPE);
|
|
|
|
|
|
if (player == thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
match_neighbor();
|
|
|
|
|
|
match_possession();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
match_possession();
|
|
|
|
|
|
}
|
|
|
|
|
|
result1 = match_result();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
if (!Good_obj(result1))
|
|
|
|
|
|
{
|
|
|
|
|
|
dflt = promote_dflt(dflt, result1);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we don't control it and it is either dark or opaque, skip past.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool control = Controls(player, result1);
|
|
|
|
|
|
if ( ( Dark(result1)
|
|
|
|
|
|
|| Opaque(result1))
|
|
|
|
|
|
&& !control)
|
|
|
|
|
|
{
|
|
|
|
|
|
dflt = promote_dflt(dflt, NOTHING);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validate object has the ENTER bit set, if requested.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( check_enter
|
|
|
|
|
|
&& !Enter_ok(result1)
|
|
|
|
|
|
&& !control)
|
|
|
|
|
|
{
|
|
|
|
|
|
dflt = promote_dflt(dflt, NOPERM);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Look for the object in the container.
|
|
|
|
|
|
//
|
|
|
|
|
|
init_match(result1, target, NOTYPE);
|
|
|
|
|
|
match_possession();
|
|
|
|
|
|
result = match_result();
|
|
|
|
|
|
result = match_possessed(player, result1, target, result, check_enter);
|
|
|
|
|
|
if (Good_obj(result))
|
|
|
|
|
|
{
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
dflt = promote_dflt(dflt, result);
|
|
|
|
|
|
}
|
|
|
|
|
|
return dflt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* parse_range: break up <what>,<low>,<high> syntax
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
void parse_range(UTF8 **name, dbref *low_bound, dbref *high_bound)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *buff1 = *name;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (buff1 && *buff1)
|
|
|
|
|
|
{
|
|
|
|
|
|
*name = parse_to(&buff1, ',', EV_STRIP_TS);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (buff1 && *buff1)
|
|
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *buff2 = parse_to(&buff1, ',', EV_STRIP_TS);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (buff1 && *buff1)
|
|
|
|
|
|
{
|
|
|
|
|
|
while (mux_isspace(*buff1))
|
|
|
|
|
|
{
|
|
|
|
|
|
buff1++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*buff1 == NUMBER_TOKEN)
|
|
|
|
|
|
{
|
|
|
|
|
|
buff1++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
|
|
|
|
*high_bound = mux_atoi64(buff1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (*high_bound >= mudstate.db_top)
|
|
|
|
|
|
{
|
|
|
|
|
|
*high_bound = mudstate.db_top - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*high_bound = mudstate.db_top - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (mux_isspace(*buff2))
|
|
|
|
|
|
{
|
|
|
|
|
|
buff2++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*buff2 == NUMBER_TOKEN)
|
|
|
|
|
|
{
|
|
|
|
|
|
buff2++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
|
|
|
|
*low_bound = mux_atoi64(buff2);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (*low_bound < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
*low_bound = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*low_bound = 0;
|
|
|
|
|
|
*high_bound = mudstate.db_top - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-04-09 05:56:51 +00:00
|
|
|
|
bool parse_thing_slash(dbref player, const UTF8 *thing, const UTF8 **after, dbref *it)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Get name up to '/'.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
while ( thing[i] != '\0'
|
|
|
|
|
|
&& thing[i] != '/')
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-04-09 05:56:51 +00:00
|
|
|
|
i++;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If no '/' in string, return failure.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
if (thing[i] == '\0')
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
*after = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*it = NOTHING;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2007-04-09 05:56:51 +00:00
|
|
|
|
*after = thing + i + 1;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Look for the object.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
init_match(player, thing, i, NOTYPE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
*it = match_result();
|
|
|
|
|
|
|
|
|
|
|
|
// Return status of search.
|
|
|
|
|
|
//
|
|
|
|
|
|
return Good_obj(*it);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-04-09 04:36:31 +00:00
|
|
|
|
bool get_obj_and_lock(dbref player, const UTF8 *what, dbref *it, ATTR **attr, UTF8 *errmsg, UTF8 **bufc)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-04-09 04:36:31 +00:00
|
|
|
|
// Get name up to '/'.
|
|
|
|
|
|
//
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
while ( what[i] != '\0'
|
|
|
|
|
|
&& what[i] != '/')
|
|
|
|
|
|
{
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-04-09 04:36:31 +00:00
|
|
|
|
*it = match_thing_quiet(player, what, i);
|
|
|
|
|
|
if (!Good_obj(*it))
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_match_result(*it, errmsg, bufc);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int anum;
|
|
|
|
|
|
if (what[i] == '/')
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// <obj>/<lock> syntax, use the named lock.
|
|
|
|
|
|
//
|
2007-04-09 04:36:31 +00:00
|
|
|
|
if (!search_nametab(player, lock_sw, what + i + 1, &anum))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 00:47:44 +00:00
|
|
|
|
safe_str(S_("#-1 LOCK NOT FOUND"), errmsg, bufc);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Not <obj>/<lock>, do a normal get of the default lock.
|
|
|
|
|
|
//
|
|
|
|
|
|
anum = A_LOCK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the attribute definition, fail if not found.
|
|
|
|
|
|
//
|
|
|
|
|
|
*attr = atr_num(anum);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr == *attr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 00:47:44 +00:00
|
|
|
|
safe_str(S_("#-1 LOCK NOT FOUND"), errmsg, bufc);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// bCanReadAttr, bCanSetAttr: Verify permission to affect attributes.
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
bool bCanReadAttr(dbref executor, dbref target, ATTR *tattr, bool bCheckParent)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
|
|
|
|
|
|
if ( !mudstate.bStandAlone
|
|
|
|
|
|
&& bCheckParent)
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_pget_info(target, tattr->number, &aowner, &aflags);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_get_info(target, tattr->number, &aowner, &aflags);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-31 10:57:44 -08:00
|
|
|
|
int test_flags = tattr->flags;
|
|
|
|
|
|
|
|
|
|
|
|
if (mudstate.attrperm_list)
|
|
|
|
|
|
{
|
|
|
|
|
|
ATTRPERM *perm_walk = mudstate.attrperm_list;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
while (nullptr != perm_walk)
|
2010-12-31 10:57:44 -08:00
|
|
|
|
{
|
|
|
|
|
|
if (quick_wild(perm_walk->wildcard, tattr->name))
|
|
|
|
|
|
{
|
|
|
|
|
|
test_flags |= perm_walk->flags;
|
|
|
|
|
|
}
|
|
|
|
|
|
perm_walk = perm_walk->next;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int mAllow = AF_VISUAL;
|
2010-12-31 10:57:44 -08:00
|
|
|
|
if ( (test_flags & mAllow)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| (aflags & mAllow))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( mudstate.bStandAlone
|
|
|
|
|
|
|| tattr->number != A_DESC
|
|
|
|
|
|
|| mudconf.read_rem_desc
|
|
|
|
|
|
|| nearby(executor, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
int mDeny = 0;
|
|
|
|
|
|
if (WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_DARK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( Owner(executor) == aowner
|
|
|
|
|
|
|| Examinable(executor, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_DARK|AF_MDARK;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mDeny)
|
|
|
|
|
|
{
|
2010-12-31 10:57:44 -08:00
|
|
|
|
if ( (test_flags & mDeny)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| (aflags & mDeny))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bCanSetAttr(dbref executor, dbref target, ATTR *tattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
if (NoModify(target) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST;
|
|
|
|
|
|
if (!God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (God(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Wizard(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST|AF_LOCK|AF_GOD;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Controls(executor, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST|AF_LOCK|AF_WIZARD|AF_GOD;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2010-12-31 10:57:44 -08:00
|
|
|
|
bool info = atr_get_info(target, tattr->number, &aowner, &aflags);
|
|
|
|
|
|
int test_flags = tattr->flags;
|
|
|
|
|
|
|
|
|
|
|
|
if (mudstate.attrperm_list)
|
|
|
|
|
|
{
|
|
|
|
|
|
ATTRPERM *perm_walk = mudstate.attrperm_list;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
while (nullptr != perm_walk)
|
2010-12-31 10:57:44 -08:00
|
|
|
|
{
|
|
|
|
|
|
if (quick_wild(perm_walk->wildcard,tattr->name))
|
|
|
|
|
|
{
|
|
|
|
|
|
test_flags |= perm_walk->flags;
|
|
|
|
|
|
}
|
|
|
|
|
|
perm_walk = perm_walk->next;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( (test_flags & mDeny)
|
|
|
|
|
|
|| (info && (aflags & mDeny)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bCanLockAttr(dbref executor, dbref target, ATTR *tattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
if (NoModify(target) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST;
|
|
|
|
|
|
if (!God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (God(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Wizard(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST|AF_GOD;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
mDeny = AF_INTERNAL|AF_IS_LOCK|AF_CONST|AF_WIZARD|AF_GOD;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2010-12-31 10:57:44 -08:00
|
|
|
|
bool info = atr_get_info(target, tattr->number, &aowner, &aflags);
|
|
|
|
|
|
int test_flags = tattr->flags;
|
|
|
|
|
|
|
|
|
|
|
|
if (mudstate.attrperm_list)
|
|
|
|
|
|
{
|
|
|
|
|
|
ATTRPERM *perm_walk = mudstate.attrperm_list;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
while (nullptr != perm_walk)
|
2010-12-31 10:57:44 -08:00
|
|
|
|
{
|
|
|
|
|
|
if (quick_wild(perm_walk->wildcard,tattr->name))
|
|
|
|
|
|
{
|
|
|
|
|
|
test_flags |= perm_walk->flags;
|
|
|
|
|
|
}
|
|
|
|
|
|
perm_walk = perm_walk->next;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( (test_flags & mDeny)
|
|
|
|
|
|
|| !info
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| (aflags & mDeny))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( Wizard(executor)
|
|
|
|
|
|
|| Owner(executor) == aowner)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* where_is: Returns place where obj is linked into a list.
|
|
|
|
|
|
* ie. location for players/things, source for exits, NOTHING for rooms.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
dbref where_is(dbref what)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Good_obj(what))
|
|
|
|
|
|
{
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref loc;
|
|
|
|
|
|
switch (Typeof(what))
|
|
|
|
|
|
{
|
|
|
|
|
|
case TYPE_PLAYER:
|
|
|
|
|
|
case TYPE_THING:
|
|
|
|
|
|
loc = Location(what);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TYPE_EXIT:
|
|
|
|
|
|
loc = Exits(what);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
loc = NOTHING;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
return loc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* where_room: Return room containing player, or NOTHING if no room or
|
|
|
|
|
|
* recursion exceeded. If player is a room, returns itself.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
dbref where_room(dbref what)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int count = mudconf.ntfy_nest_lim; count > 0; count--)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Good_obj(what))
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isRoom(what))
|
|
|
|
|
|
{
|
|
|
|
|
|
return what;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Has_location(what))
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
what = Location(what);
|
|
|
|
|
|
}
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool locatable(dbref player, dbref it, dbref enactor)
|
|
|
|
|
|
{
|
2020-08-10 02:57:24 -06:00
|
|
|
|
// No sense in trying to locate a bad object.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
|
|
|
|
|
if (!Good_obj(it))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref loc_it = where_is(it);
|
|
|
|
|
|
|
|
|
|
|
|
// Succeed if we can examine the target, if we are the target, if we can
|
2026-03-03 20:30:04 -07:00
|
|
|
|
// examine the location, if the player is a wizard, or if the target
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// caused the lookup.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( Examinable(player, it)
|
|
|
|
|
|
|| Find_Unfindable(player)
|
|
|
|
|
|
|| loc_it == player
|
|
|
|
|
|
|| ( loc_it != NOTHING
|
|
|
|
|
|
&& ( Examinable(player, loc_it)
|
2020-08-10 02:57:24 -06:00
|
|
|
|
|| loc_it == where_is(player))
|
|
|
|
|
|
&& ( !Hidden(it)
|
|
|
|
|
|
|| See_Hidden(player)))
|
2026-03-03 20:30:04 -07:00
|
|
|
|
|| Wizard(player)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| it == enactor)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref room_it = where_room(it);
|
|
|
|
|
|
bool findable_room;
|
|
|
|
|
|
if (Good_obj(room_it))
|
|
|
|
|
|
{
|
|
|
|
|
|
findable_room = Findable(room_it);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
findable_room = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Succeed if we control the containing room or if the target is findable
|
|
|
|
|
|
// and the containing room is not unfindable.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( ( room_it != NOTHING
|
|
|
|
|
|
&& Examinable(player, room_it))
|
2020-08-10 02:57:24 -06:00
|
|
|
|
&& ( !Hidden(it)
|
|
|
|
|
|
|| See_Hidden(player))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| Find_Unfindable(player)
|
|
|
|
|
|
|| ( Findable(it)
|
2020-08-10 02:57:24 -06:00
|
|
|
|
&& findable_room)
|
|
|
|
|
|
&& ( !Hidden(it)
|
|
|
|
|
|
|| See_Hidden(player)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We can't do it.
|
|
|
|
|
|
//
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* nearby: Check if thing is nearby player (in inventory, in same room, or
|
|
|
|
|
|
* IS the room.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
bool nearby(dbref player, dbref thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !Good_obj(player)
|
|
|
|
|
|
|| !Good_obj(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( Can_Hide(thing)
|
|
|
|
|
|
&& Hidden(thing)
|
|
|
|
|
|
&& !See_Hidden(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
dbref thing_loc = where_is(thing);
|
|
|
|
|
|
if (thing_loc == player)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
dbref player_loc = where_is(player);
|
|
|
|
|
|
if ( thing_loc == player_loc
|
|
|
|
|
|
|| thing == player_loc)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * exit_visible, exit_displayable: Is exit visible?
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool exit_visible(dbref exit, dbref player, int key)
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef WOD_REALMS
|
|
|
|
|
|
if (!mudstate.bStandAlone)
|
|
|
|
|
|
{
|
|
|
|
|
|
int iRealmDirective = DoThingToThingVisibility(player, exit,
|
|
|
|
|
|
ACTION_IS_STATIONARY);
|
|
|
|
|
|
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // WOD_REALMS
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
|
|
|
|
|
if (!mudstate.bStandAlone)
|
|
|
|
|
|
{
|
2007-07-29 20:55:49 -07:00
|
|
|
|
if (!IsReal(player, exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2006-11-27 05:25:42 +00:00
|
|
|
|
#endif // REALITY_LVLS
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Exam exit's location
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( (key & VE_LOC_XAM)
|
|
|
|
|
|
|| Examinable(player, exit)
|
|
|
|
|
|
|| Light(exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Dark location or base
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( (key & (VE_LOC_DARK | VE_BASE_DARK))
|
|
|
|
|
|
|| Dark(exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:11:36 -07:00
|
|
|
|
// VisibleLock
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!could_doit(player, exit, A_LVISIBLE))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Default
|
|
|
|
|
|
//
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Exit visible to look
|
|
|
|
|
|
//
|
|
|
|
|
|
bool exit_displayable(dbref exit, dbref player, int key)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Dark exit
|
|
|
|
|
|
//
|
|
|
|
|
|
if (Dark(exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WOD_REALMS
|
|
|
|
|
|
if (!mudstate.bStandAlone)
|
|
|
|
|
|
{
|
|
|
|
|
|
int iRealmDirective = DoThingToThingVisibility(player, exit,
|
|
|
|
|
|
ACTION_IS_STATIONARY);
|
|
|
|
|
|
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // WOD_REALMS
|
|
|
|
|
|
|
2007-07-29 20:55:49 -07:00
|
|
|
|
#ifdef REALITY_LVLS
|
|
|
|
|
|
if (!mudstate.bStandAlone)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!IsReal(player, exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // REALITY_LVLS
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Light exit
|
|
|
|
|
|
//
|
|
|
|
|
|
if (Light(exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Dark location or base.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (key & (VE_LOC_DARK | VE_BASE_DARK))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:11:36 -07:00
|
|
|
|
// VisibleLock
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!could_doit(player, exit, A_LVISIBLE))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Default
|
|
|
|
|
|
//
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* did_it: Have player do something to/with thing
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
void did_it(dbref player, dbref thing, int what, const UTF8 *def, int owhat,
|
|
|
|
|
|
const UTF8 *odef, int awhat, int ctrl_flags,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
const UTF8 *args[], int nargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-06-03 03:27:25 -06:00
|
|
|
|
if (alarm_clock.alarmed)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *d, *buff, *act, *charges, *bp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
dbref loc, aowner;
|
2026-07-28 19:34:54 -06:00
|
|
|
|
int64_t num;
|
|
|
|
|
|
int aflags;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// If we need to call exec() from within this function, we first save
|
|
|
|
|
|
// the state of the global registers, in order to avoid munging them
|
|
|
|
|
|
// inappropriately. Do note that the restoration to their original
|
|
|
|
|
|
// values occurs BEFORE the execution of the @a-attribute. Therefore,
|
|
|
|
|
|
// any changing of setq() values done in the @-attribute and @o-attribute
|
|
|
|
|
|
// will NOT be passed on. This prevents odd behaviors that result from
|
|
|
|
|
|
// odd @verbs and so forth (the idea is to preserve the caller's control
|
|
|
|
|
|
// of the global register values).
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
bool need_pres = false;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
reg_ref **preserve = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// message to player.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (what > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
d = atr_pget(thing, what, &aowner, &aflags);
|
|
|
|
|
|
if (*d)
|
|
|
|
|
|
{
|
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
|
|
|
|
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
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
|
|
|
|
// Output raw text with no substitutions.
|
|
|
|
|
|
//
|
|
|
|
|
|
notify(player, d);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
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
|
|
|
|
need_pres = true;
|
|
|
|
|
|
preserve = PushRegisters(MAX_GLOBAL_REGS);
|
|
|
|
|
|
save_global_regs(preserve);
|
|
|
|
|
|
|
|
|
|
|
|
buff = bp = alloc_lbuf("did_it.1");
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, player, player,
|
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
|
|
|
|
AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_FCHECK|EV_TOP),
|
|
|
|
|
|
args, nargs);
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
if ( (aflags & AF_HTML)
|
|
|
|
|
|
&& Html(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_str(T("\r\n"), buff, &bp);
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify_html(player, buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(buff);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (def)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, def);
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(d);
|
|
|
|
|
|
}
|
2007-10-09 11:41:14 -07:00
|
|
|
|
else if (what < 0 && def)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
notify(player, def);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// message to neighbors.
|
|
|
|
|
|
//
|
2006-11-27 04:06:15 +00:00
|
|
|
|
if ( 0 < owhat
|
2006-09-01 22:59:23 +00:00
|
|
|
|
&& Has_location(player)
|
|
|
|
|
|
&& Good_obj(loc = Location(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
d = atr_pget(thing, owhat, &aowner, &aflags);
|
|
|
|
|
|
if (*d)
|
|
|
|
|
|
{
|
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
|
|
|
|
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Output raw text with no substitutions.
|
|
|
|
|
|
//
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
|
|
|
|
|
if (aflags & AF_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing, d);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing,
|
|
|
|
|
|
tprintf(T("%s %s"), Moniker(player), d));
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
if (aflags & AF_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing, d);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing,
|
|
|
|
|
|
tprintf(T("%s %s"), Moniker(player), d));
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // REALITY_LVLS
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!need_pres)
|
|
|
|
|
|
{
|
|
|
|
|
|
need_pres = true;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
preserve = PushRegisters(MAX_GLOBAL_REGS);
|
|
|
|
|
|
save_global_regs(preserve);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
buff = bp = alloc_lbuf("did_it.2");
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, player, player,
|
2006-09-06 04:52:56 +00:00
|
|
|
|
AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_FCHECK|EV_TOP),
|
2007-01-28 02:08:08 +00:00
|
|
|
|
args, nargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
if (*buff)
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2006-11-27 04:06:15 +00:00
|
|
|
|
if (aflags & AF_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing, buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing,
|
2008-12-27 18:37:01 -08:00
|
|
|
|
tprintf(T("%s %s"), Moniker(player), buff));
|
2006-11-27 04:06:15 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2006-11-27 04:06:15 +00:00
|
|
|
|
if (aflags & AF_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing, buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing,
|
2008-12-27 18:37:01 -08:00
|
|
|
|
tprintf(T("%s %s"), Moniker(player), buff));
|
2006-11-27 04:06:15 +00:00
|
|
|
|
}
|
2006-11-27 05:25:42 +00:00
|
|
|
|
#endif // REALITY_LVLS
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(buff);
|
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
|
|
|
|
} // else (not NOEVAL)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (odef)
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2006-11-27 05:25:42 +00:00
|
|
|
|
if (ctrl_flags & VERB_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing, odef);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-09-21 08:03:04 -07:00
|
|
|
|
notify_except2_rlevel(loc, player, player, thing,
|
2008-12-27 18:37:01 -08:00
|
|
|
|
tprintf(T("%s %s"), Moniker(player), odef));
|
2006-11-27 05:25:42 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2006-11-27 05:25:42 +00:00
|
|
|
|
if (ctrl_flags & VERB_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing, odef);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-09-21 08:03:04 -07:00
|
|
|
|
notify_except2(loc, player, player, thing,
|
2008-12-27 18:37:01 -08:00
|
|
|
|
tprintf(T("%s %s"), Moniker(player), odef));
|
2006-11-27 05:25:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
#endif // REALITY_LVLS
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(d);
|
2006-11-27 05:25:42 +00:00
|
|
|
|
} else if ( owhat < 0
|
|
|
|
|
|
&& odef
|
|
|
|
|
|
&& Has_location(player)
|
|
|
|
|
|
&& Good_obj(loc = Location(player)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2006-11-27 05:25:42 +00:00
|
|
|
|
if (ctrl_flags & VERB_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2_rlevel(loc, player, player, thing, odef);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
notify_except2_rlevel(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef));
|
2006-11-27 05:25:42 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2006-11-27 05:25:42 +00:00
|
|
|
|
if (ctrl_flags & VERB_NONAME)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except2(loc, player, player, thing, odef);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
notify_except2(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef));
|
2006-11-27 05:25:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
#endif // REALITY_LVLS
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we preserved the state of the global registers, restore them.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (need_pres)
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
restore_global_regs(preserve);
|
|
|
|
|
|
PopRegisters(preserve, MAX_GLOBAL_REGS);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-11-27 04:06:15 +00:00
|
|
|
|
// Do the action attribute.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2006-11-27 04:06:15 +00:00
|
|
|
|
if ( 0 < awhat
|
|
|
|
|
|
&& IsReal(thing, player))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2006-11-27 04:06:15 +00:00
|
|
|
|
if (0 < awhat)
|
2006-11-27 05:25:42 +00:00
|
|
|
|
#endif // REALITY_LVLS
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (*(act = atr_pget(thing, awhat, &aowner, &aflags)))
|
|
|
|
|
|
{
|
2006-09-06 05:46:01 +00:00
|
|
|
|
dbref aowner2;
|
|
|
|
|
|
int aflags2;
|
|
|
|
|
|
charges = atr_pget(thing, A_CHARGES, &aowner2, &aflags2);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (*charges)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// int64_t, not int (#1402).
|
|
|
|
|
|
//
|
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
|
|
|
|
num = mux_atoi64(charges);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (num > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
buff = alloc_sbuf("did_it.charges");
|
2026-07-28 19:34:54 -06:00
|
|
|
|
mux_i64toa(num - 1, buff);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_add_raw(thing, A_CHARGES, buff);
|
|
|
|
|
|
free_sbuf(buff);
|
|
|
|
|
|
}
|
2006-09-06 05:46:01 +00:00
|
|
|
|
else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner2, &aflags2)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(act);
|
|
|
|
|
|
act = buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(act);
|
|
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
free_lbuf(charges);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(charges);
|
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
|
|
|
|
if (!((aflags & AF_NOEVAL) || NoEval(thing)))
|
|
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeAbsolute lta;
|
|
|
|
|
|
wait_que(thing, player, player, AttrTrace(aflags, 0), false, lta,
|
|
|
|
|
|
NOTHING, 0,
|
|
|
|
|
|
act,
|
|
|
|
|
|
nargs, args,
|
|
|
|
|
|
mudstate.global_regs);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(act);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* do_verb: Command interface to did_it.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2006-12-27 17:53:42 +00:00
|
|
|
|
void do_verb(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UTF8 *victim_str, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2006-12-27 17:53:42 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Look for the victim.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !victim_str
|
|
|
|
|
|
|| !*victim_str)
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify(executor, M_("Nothing to do."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the victim.
|
|
|
|
|
|
//
|
|
|
|
|
|
init_match(executor, victim_str, NOTYPE);
|
|
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
dbref victim = noisy_match_result();
|
|
|
|
|
|
if (!Good_obj(victim))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the actor. Default is my cause.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref actor;
|
|
|
|
|
|
if ( nargs >= 1
|
|
|
|
|
|
&& args[0] && *args[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
init_match(executor, args[0], NOTYPE);
|
|
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
actor = noisy_match_result();
|
|
|
|
|
|
if (!Good_obj(actor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
actor = enactor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check permissions. There are two possibilities:
|
|
|
|
|
|
//
|
|
|
|
|
|
// 1. Executor controls both victim and actor. In this case,
|
|
|
|
|
|
// victim runs his action list.
|
|
|
|
|
|
//
|
|
|
|
|
|
// 2. Executor controls actor. In this case victim does not run
|
|
|
|
|
|
// his action list and any attributes that executor cannot read
|
|
|
|
|
|
// from victim are defaulted.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!Controls(executor, actor))
|
|
|
|
|
|
{
|
2026-07-27 11:49:19 +00:00
|
|
|
|
notify_quiet(executor, M_("Permission denied,"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
int what = -1;
|
|
|
|
|
|
int owhat = -1;
|
|
|
|
|
|
int awhat = -1;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
const UTF8 *whatd = nullptr;
|
|
|
|
|
|
const UTF8 *owhatd = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int nxargs = 0;
|
|
|
|
|
|
dbref aowner = NOTHING;
|
|
|
|
|
|
int aflags = NOTHING;
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *xargs[10];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
switch (nargs) // Yes, this IS supposed to fall through.
|
|
|
|
|
|
{
|
|
|
|
|
|
case 7:
|
|
|
|
|
|
// Get arguments.
|
|
|
|
|
|
//
|
2007-04-09 08:36:13 +00:00
|
|
|
|
parse_arglist(victim, actor, actor, args[6],
|
2018-10-03 17:54:51 +00:00
|
|
|
|
EV_STRIP_LS | EV_STRIP_TS, xargs, 10, nullptr, 0, &nxargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
|
// Get action attribute.
|
|
|
|
|
|
//
|
2007-03-14 19:32:44 +00:00
|
|
|
|
ap = atr_str(args[5]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
awhat = ap->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
|
|
// Get others message default.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (args[4] && *args[4])
|
|
|
|
|
|
{
|
|
|
|
|
|
owhatd = args[4];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
// Get others message attribute.
|
|
|
|
|
|
//
|
2007-03-14 19:32:44 +00:00
|
|
|
|
ap = atr_str(args[3]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (ap && (ap->number > 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
owhat = ap->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
// Get enactor message default.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (args[2] && *args[2])
|
|
|
|
|
|
{
|
|
|
|
|
|
whatd = args[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
// Get enactor message attribute.
|
|
|
|
|
|
//
|
2007-03-14 19:32:44 +00:00
|
|
|
|
ap = atr_str(args[1]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (ap && (ap->number > 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
what = ap->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If executor doesn't control both, enforce visibility restrictions.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!Controls(executor, victim))
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
ap = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (what != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_get_info(victim, what, &aowner, &aflags);
|
|
|
|
|
|
ap = atr_num(what);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !ap
|
|
|
|
|
|
|| !bCanReadAttr(executor, victim, ap, false)
|
|
|
|
|
|
|| ( ap->number == A_DESC
|
|
|
|
|
|
&& !mudconf.read_rem_desc
|
|
|
|
|
|
&& !Examinable(executor, victim)
|
|
|
|
|
|
&& !nearby(executor, victim)))
|
|
|
|
|
|
{
|
|
|
|
|
|
what = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
ap = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (owhat != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_get_info(victim, owhat, &aowner, &aflags);
|
|
|
|
|
|
ap = atr_num(owhat);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !ap
|
|
|
|
|
|
|| !bCanReadAttr(executor, victim, ap, false)
|
|
|
|
|
|
|| ( ap->number == A_DESC
|
|
|
|
|
|
&& !mudconf.read_rem_desc
|
|
|
|
|
|
&& !Examinable(executor, victim)
|
|
|
|
|
|
&& !nearby(executor, victim)))
|
|
|
|
|
|
{
|
|
|
|
|
|
owhat = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
awhat = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Go do it.
|
|
|
|
|
|
//
|
2006-11-27 05:25:42 +00:00
|
|
|
|
did_it(actor, victim, what, whatd, owhat, owhatd, awhat,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
key & VERB_NONAME, (const UTF8 **)xargs, nxargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Free user args.
|
|
|
|
|
|
//
|
|
|
|
|
|
for (int i = 0; i < nxargs; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(xargs[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 19:40:40 -06:00
|
|
|
|
// AssertionFailed and OutOfMemory moved to libmux.cpp.
|
2007-09-17 23:56:51 +00:00
|
|
|
|
|
2007-10-20 16:24:05 +00:00
|
|
|
|
static void ListReferences(dbref executor, UTF8 *reference_name)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref target = NOTHING;
|
|
|
|
|
|
bool global_only = false;
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ( nullptr == reference_name
|
2007-09-21 08:23:35 -07:00
|
|
|
|
|| '\0' == reference_name[0])
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
global_only = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
global_only = false;
|
2008-02-28 07:15:19 +00:00
|
|
|
|
target = lookup_player(executor, reference_name, 1);
|
|
|
|
|
|
if (!Good_obj(target))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("No such player."));
|
2008-02-28 07:15:19 +00:00
|
|
|
|
return;
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
if (!Controls(executor, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
raw_notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
// Listing:
|
2007-09-17 23:56:51 +00:00
|
|
|
|
// - if global_only is true, list all references that begin with _
|
|
|
|
|
|
// - Otherwise, list all references whose owner is target
|
|
|
|
|
|
//
|
|
|
|
|
|
reference_entry *htab_entry;
|
|
|
|
|
|
bool match_found = false;
|
|
|
|
|
|
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
for (auto &[ref_key, ref_val] : mudstate.reference_htab)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
htab_entry = static_cast<reference_entry*>(ref_val);
|
2008-02-28 07:15:19 +00:00
|
|
|
|
if ( ( global_only
|
|
|
|
|
|
&& '_' == htab_entry->name[0])
|
2011-12-02 16:45:29 -08:00
|
|
|
|
|| ( !global_only
|
|
|
|
|
|
&& target == htab_entry->owner))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (!Good_obj(htab_entry->target))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 11:37:23 -06:00
|
|
|
|
// @list ref schema (#1667 Phase 4 C5): name 12 / target 20 / owner 20.
|
|
|
|
|
|
//
|
|
|
|
|
|
static const size_t kRefNameCols = 12;
|
|
|
|
|
|
static const size_t kRefTargetCols = 20;
|
|
|
|
|
|
static const size_t kRefOwnerCols = 20;
|
|
|
|
|
|
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (!match_found)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
match_found = true;
|
2026-07-28 11:37:23 -06:00
|
|
|
|
UTF8 header[LBUF_SIZE];
|
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
pos = mux_table_append_ljust(header, sizeof(header), pos,
|
|
|
|
|
|
M_("Reference"), kRefNameCols);
|
|
|
|
|
|
pos = mux_table_append_bytes(header, sizeof(header), pos, " ");
|
|
|
|
|
|
pos = mux_table_append_ljust(header, sizeof(header), pos,
|
|
|
|
|
|
M_("Target"), kRefTargetCols);
|
|
|
|
|
|
pos = mux_table_append_bytes(header, sizeof(header), pos, " ");
|
|
|
|
|
|
pos = mux_table_append_ljust(header, sizeof(header), pos,
|
|
|
|
|
|
M_("Owner"), kRefOwnerCols);
|
|
|
|
|
|
raw_notify(executor, header);
|
2007-09-21 08:03:04 -07:00
|
|
|
|
raw_notify(executor,
|
2026-07-27 12:39:20 +00:00
|
|
|
|
M_("-------------------------------------------------------"));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-21 08:03:04 -07:00
|
|
|
|
UTF8 *object_buf =
|
2007-09-17 23:56:51 +00:00
|
|
|
|
unparse_object(executor, htab_entry->target, false);
|
|
|
|
|
|
|
2026-07-28 11:37:23 -06:00
|
|
|
|
UTF8 line[LBUF_SIZE];
|
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
pos = mux_table_append_ljust(line, sizeof(line), pos,
|
|
|
|
|
|
htab_entry->name, kRefNameCols);
|
|
|
|
|
|
pos = mux_table_append_bytes(line, sizeof(line), pos, " ");
|
|
|
|
|
|
pos = mux_table_append_ljust(line, sizeof(line), pos,
|
|
|
|
|
|
object_buf, kRefTargetCols);
|
|
|
|
|
|
pos = mux_table_append_bytes(line, sizeof(line), pos, " ");
|
|
|
|
|
|
pos = mux_table_append_ljust(line, sizeof(line), pos,
|
|
|
|
|
|
Moniker(htab_entry->owner), kRefOwnerCols);
|
|
|
|
|
|
raw_notify(executor, line);
|
2007-09-17 23:56:51 +00:00
|
|
|
|
|
|
|
|
|
|
free_lbuf(object_buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (!match_found)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("GAME: No references found."));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-09-21 08:03:04 -07:00
|
|
|
|
raw_notify(executor,
|
2026-07-27 12:39:20 +00:00
|
|
|
|
M_("---------------- End of Reference List ----------------"));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-10-20 16:24:05 +00:00
|
|
|
|
void do_reference
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
|
|
|
|
|
int eval,
|
|
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
|
|
|
|
|
UTF8 *reference_name,
|
|
|
|
|
|
UTF8 *object_name,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
|
|
|
|
|
)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2007-10-20 16:24:05 +00:00
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(eval);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (key & REFERENCE_LIST)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2007-10-20 16:24:05 +00:00
|
|
|
|
ListReferences(executor, reference_name);
|
2007-09-17 23:56:51 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
// References can only be set on objects the executor can examine.
|
|
|
|
|
|
//
|
2011-12-02 16:45:29 -08:00
|
|
|
|
dbref target = NOTHING;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ( nullptr != object_name
|
2007-09-21 08:23:35 -07:00
|
|
|
|
&& '\0' != object_name[0])
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
target = match_thing_quiet(executor, object_name);
|
|
|
|
|
|
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (!Good_obj(target))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOMATCH_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2011-12-02 16:45:29 -08:00
|
|
|
|
else if (!Examinable(executor, target))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
if ('_' == reference_name[0])
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (!Wizard(executor))
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-12 21:31:08 -06:00
|
|
|
|
|
2026-03-23 21:52:18 -06:00
|
|
|
|
LBuf tbuf = LBuf_Src("do_name");
|
2026-07-28 19:59:35 -06:00
|
|
|
|
// mux_snprintf returns length written (0..count-1), not "would have".
|
|
|
|
|
|
//
|
|
|
|
|
|
size_t tbuf_len;
|
2026-03-12 21:31:08 -06:00
|
|
|
|
if ('_' == reference_name[0])
|
|
|
|
|
|
{
|
2026-07-28 19:59:35 -06:00
|
|
|
|
tbuf_len = mux_snprintf(tbuf.get(), LBUF_SIZE, T("%s"),
|
|
|
|
|
|
reference_name);
|
2026-03-12 21:31:08 -06:00
|
|
|
|
}
|
2007-09-17 23:56:51 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-28 19:59:35 -06:00
|
|
|
|
tbuf_len = mux_snprintf(tbuf.get(), LBUF_SIZE, T("%s.%lld"),
|
|
|
|
|
|
reference_name, static_cast<long long>(executor));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
2026-03-23 21:52:18 -06:00
|
|
|
|
auto it_ref = mudstate.reference_htab.find(std::vector<UTF8>(tbuf.get(), tbuf.get() + tbuf_len));
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
struct reference_entry *result = (it_ref != mudstate.reference_htab.end())
|
|
|
|
|
|
? static_cast<reference_entry*>(it_ref->second) : nullptr;
|
2007-09-21 08:03:04 -07:00
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
enum { Delete, Add, Update, NotFound, Redundant, OutOfMemory } eOperation;
|
2007-09-17 23:56:51 +00:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != result)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2007-09-21 08:23:35 -07:00
|
|
|
|
if (NOTHING == target)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2008-02-28 07:15:19 +00:00
|
|
|
|
eOperation = Delete;
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
2007-09-21 08:23:35 -07:00
|
|
|
|
else if (result->target == target)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2008-02-28 07:15:19 +00:00
|
|
|
|
eOperation = Redundant;
|
|
|
|
|
|
}
|
|
|
|
|
|
else // if (result->target != target)
|
|
|
|
|
|
{
|
|
|
|
|
|
eOperation = Update;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (NOTHING == target)
|
|
|
|
|
|
{
|
|
|
|
|
|
eOperation = NotFound;
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2008-02-28 07:15:19 +00:00
|
|
|
|
eOperation = Add;
|
2009-11-28 20:48:14 -08:00
|
|
|
|
if ( !Wizard(executor)
|
|
|
|
|
|
&& ThrottleReferences(executor))
|
|
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("References requested too quickly."));
|
2009-11-28 20:48:14 -08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
if ( Delete == eOperation
|
|
|
|
|
|
|| Update == eOperation)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2008-02-28 07:15:19 +00:00
|
|
|
|
// Release the existing reference.
|
|
|
|
|
|
//
|
|
|
|
|
|
MEMFREE(result->name);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
result->name = nullptr;
|
2008-02-28 07:15:19 +00:00
|
|
|
|
MEMFREE(result);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
result = nullptr;
|
2026-03-23 21:52:18 -06:00
|
|
|
|
mudstate.reference_htab.erase(std::vector<UTF8>(tbuf.get(), tbuf.get() + tbuf_len));
|
2008-02-28 07:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( Update == eOperation
|
|
|
|
|
|
|| Add == eOperation)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
result = static_cast<reference_entry *>(MEMALLOC(sizeof(reference_entry)));
|
2008-02-28 07:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch(...)
|
|
|
|
|
|
{
|
|
|
|
|
|
; // Nothing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != result)
|
2008-02-28 07:15:19 +00:00
|
|
|
|
{
|
|
|
|
|
|
result->target = target;
|
|
|
|
|
|
result->owner = executor;
|
|
|
|
|
|
result->name = StringCloneLen(tbuf, tbuf_len);
|
2026-03-23 21:52:18 -06:00
|
|
|
|
mudstate.reference_htab.emplace(std::vector<UTF8>(tbuf.get(), tbuf.get() + tbuf_len), result);
|
2008-02-28 07:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
eOperation = OutOfMemory;
|
|
|
|
|
|
}
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-28 07:15:19 +00:00
|
|
|
|
if (Delete == eOperation)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("Reference cleared."));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
2008-02-28 07:15:19 +00:00
|
|
|
|
else if (Update == eOperation)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("Reference updated."));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
2008-02-28 07:15:19 +00:00
|
|
|
|
else if (Redundant == eOperation)
|
|
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("That reference already exists."));
|
2008-02-28 07:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (NotFound == eOperation)
|
|
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("No such reference to clear."));
|
2008-02-28 07:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (Add == eOperation)
|
2007-09-17 23:56:51 +00:00
|
|
|
|
{
|
2026-07-27 12:39:20 +00:00
|
|
|
|
raw_notify(executor, M_("Reference added."));
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|
2008-02-28 07:15:19 +00:00
|
|
|
|
else if (OutOfMemory == eOperation)
|
|
|
|
|
|
{
|
|
|
|
|
|
raw_notify(executor, OUT_OF_MEMORY);
|
|
|
|
|
|
}
|
2007-09-17 23:56:51 +00:00
|
|
|
|
}
|