mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
stubslave: The child side of the socketpair was blocking, so read() would hang when poll() woke for POLLOUT without POLLIN. Set fd 0 to O_NONBLOCK in main() so read() returns EAGAIN in that case. @switch: The iter context propagation now checks mudconf.safer_iter before capturing ##, matching @dolist's existing guard. Games with safer_iter enabled will not leak ## into queued @switch bodies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3095 lines
77 KiB
C++
3095 lines
77 KiB
C++
/*! \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.
|
|
*/
|
|
|
|
#include "copyright.h"
|
|
#include "autoconf.h"
|
|
#include "config.h"
|
|
#include "externs.h"
|
|
|
|
extern "C" {
|
|
#include "color_ops.h"
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#include "ganl_stub.h"
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* 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;
|
|
UTF8 *key = atr_get("could_doit.134", thing, locknum, &aowner, &aflags);
|
|
bool doit = eval_boolexp_atr(player, thing, thing, key);
|
|
free_lbuf(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;
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// The exception to the above is mudconf.see_own_dark which allows a
|
|
// myopic self-examination.
|
|
//
|
|
return ( ( ( can_see_loc
|
|
|| Light(thing))
|
|
&& !Dark(thing)
|
|
#ifdef REALITY_LVLS
|
|
&& IsReal(player, thing)
|
|
#endif // REALITY_LVLS
|
|
&& could_doit(player, thing, A_LVISIBLE))
|
|
|| ( mudconf.see_own_dark
|
|
&& MyopicExam(player, thing)));
|
|
}
|
|
|
|
static bool pay_quota(dbref who, int cost)
|
|
{
|
|
// If no cost, succeed
|
|
//
|
|
if (cost <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// determine quota
|
|
//
|
|
dbref aowner;
|
|
int aflags;
|
|
UTF8 *quota_str = atr_get("pay_quota.200", Owner(who), A_RQUOTA, &aowner, &aflags);
|
|
int quota = mux_atol(quota_str);
|
|
free_lbuf(quota_str);
|
|
|
|
// enough to build? Wizards always have enough.
|
|
//
|
|
quota -= cost;
|
|
if ( quota < 0
|
|
&& !Free_Quota(who)
|
|
&& !Free_Quota(Owner(who)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Dock the quota.
|
|
//
|
|
UTF8 buf[I32BUF_SIZE];
|
|
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)
|
|
{
|
|
notify(player, tprintf(T("Sorry, you don\xE2\x80\x99t have enough %s."),
|
|
mudconf.many_coins));
|
|
}
|
|
else
|
|
{
|
|
notify(player, tprintf(T("Sorry, that player doesn\xE2\x80\x99t have enough %s."),
|
|
mudconf.many_coins));
|
|
}
|
|
return false;
|
|
}
|
|
if (mudconf.quotas)
|
|
{
|
|
if (!pay_quota(who, quota))
|
|
{
|
|
if (player == who)
|
|
{
|
|
notify(player, T("Sorry, your building contract has run out."));
|
|
}
|
|
else
|
|
{
|
|
notify(player,
|
|
T("Sorry, that player\xE2\x80\x99s building contract has run out."));
|
|
}
|
|
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;
|
|
UTF8 buf[I32BUF_SIZE];
|
|
|
|
UTF8 *quota = atr_get("add_quota.288", who, A_RQUOTA, &aowner, &aflags);
|
|
mux_ltoa(mux_atol(quota) + payment, buf);
|
|
free_lbuf(quota);
|
|
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);
|
|
}
|
|
|
|
// Every character in the name must be allowed by one of the character sets mentioned.
|
|
// If no character sets are mentions, everything is allowed.
|
|
//
|
|
bool IsRestricted(const UTF8 *pName, int charset)
|
|
{
|
|
if (0 == charset)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while ('\0' != pName[0])
|
|
{
|
|
bool bAllowed = false;
|
|
if ( (ALLOW_CHARSET_ASCII & charset)
|
|
&& (0x80 & pName[0]) == 0)
|
|
{
|
|
bAllowed = true;
|
|
}
|
|
else if ( (ALLOW_CHARSET_8859_1 & charset)
|
|
&& mux_is8859_1(pName))
|
|
{
|
|
bAllowed = true;
|
|
}
|
|
else if ( (ALLOW_CHARSET_8859_2 & charset)
|
|
&& mux_is8859_2(pName))
|
|
{
|
|
bAllowed = true;
|
|
}
|
|
else if ( (ALLOW_CHARSET_HANGUL & charset)
|
|
&& mux_ishangul(pName))
|
|
{
|
|
bAllowed = true;
|
|
}
|
|
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;
|
|
}
|
|
|
|
if (!bAllowed)
|
|
{
|
|
return true;
|
|
}
|
|
pName = utf8_advance_predicate(pName);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 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).
|
|
//
|
|
UTF8 *MakeCanonicalObjectName(const UTF8 *pName, size_t *pnName, bool *pbValid, int charset)
|
|
{
|
|
static UTF8 Buf[MBUF_SIZE];
|
|
|
|
*pnName = 0;
|
|
*pbValid = false;
|
|
|
|
if (!pName)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Build up what the real name would be. If we pass all the
|
|
// checks, this is what we will return as a result.
|
|
//
|
|
mux_field fldLen = StripTabsAndTruncate(pName, Buf, MBUF_SIZE-1, MBUF_SIZE-1);
|
|
|
|
// Disallow pure ANSI names. There must be at least -something-
|
|
// visible.
|
|
//
|
|
if (0 == fldLen.m_column)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Get the stripped version (Visible parts without color info).
|
|
//
|
|
size_t nStripped;
|
|
const UTF8 *pStripped = strip_color(Buf, &nStripped);
|
|
|
|
// Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
|
|
// as the first character, or SPACE as the last character
|
|
//
|
|
if ( reinterpret_cast<const UTF8 *>(strchr(reinterpret_cast<const char *>("*!#"), pStripped[0]))
|
|
|| mux_isspace(pStripped[0])
|
|
|| mux_isspace(pStripped[nStripped-1]))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
|
|
// and OR_TOKEN are allowed.
|
|
//
|
|
const UTF8 *p = pStripped;
|
|
while ('\0' != *p)
|
|
{
|
|
if (!mux_isobjectname(p))
|
|
{
|
|
return nullptr;
|
|
}
|
|
p = utf8_advance_predicate(p);
|
|
}
|
|
|
|
// 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)))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (IsRestricted(pStripped, charset))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Reject names that would be silently truncated.
|
|
//
|
|
size_t nOrigStripped;
|
|
strip_color(pName, &nOrigStripped);
|
|
if (nOrigStripped > nStripped)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
*pnName = fldLen.m_byte;
|
|
*pbValid = true;
|
|
return Buf;
|
|
}
|
|
|
|
// The following function validates exit names.
|
|
//
|
|
UTF8 *MakeCanonicalExitName(const UTF8 *pName, size_t *pnName, bool *pbValid)
|
|
{
|
|
static UTF8 Buf[MBUF_SIZE];
|
|
|
|
*pnName = 0;
|
|
*pbValid = false;
|
|
|
|
if (!pName)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
mux_strncpy(Buf, pName, MBUF_SIZE - 1);
|
|
|
|
// Sanitize the input before processing.
|
|
//
|
|
string_token st(Buf, T(";"));
|
|
|
|
// 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.
|
|
//
|
|
UTF8 *ptr;
|
|
UTF8 clean_buf[MBUF_SIZE];
|
|
UTF8 *bp_clean = clean_buf;
|
|
bool bHaveDisplay = false;
|
|
for (ptr = st.parse(); ptr; ptr = st.parse())
|
|
{
|
|
UTF8 *pTrimmedSegment = nullptr;
|
|
if (bHaveDisplay)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
// Ignore segments which contained nothing but spaces.
|
|
//
|
|
if ('\0' != pTrimmedSegment[0])
|
|
{
|
|
bool valid = false;
|
|
size_t len = 0;
|
|
|
|
UTF8 *pValidSegment = MakeCanonicalObjectName(pTrimmedSegment, &len, &valid, mudconf.exit_name_charset);
|
|
if (valid)
|
|
{
|
|
if (bHaveDisplay)
|
|
{
|
|
safe_mb_chr_ascii(';', clean_buf, &bp_clean);
|
|
safe_mb_str(pValidSegment, clean_buf, &bp_clean);
|
|
}
|
|
else
|
|
{
|
|
safe_mb_str(pValidSegment, clean_buf, &bp_clean);
|
|
bHaveDisplay = true;
|
|
}
|
|
}
|
|
}
|
|
free_lbuf(pTrimmedSegment);
|
|
pTrimmedSegment = nullptr;
|
|
}
|
|
*bp_clean = '\0';
|
|
|
|
*pbValid = bHaveDisplay;
|
|
if (!bHaveDisplay)
|
|
{
|
|
*pnName = 0;
|
|
return Buf;
|
|
}
|
|
|
|
mux_strncpy(Buf, clean_buf, MBUF_SIZE - 1);
|
|
*pnName = mux_strlen(Buf);
|
|
|
|
return Buf;
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
bool ValidatePlayerName(const UTF8 *pName)
|
|
{
|
|
if (!pName)
|
|
{
|
|
return false;
|
|
}
|
|
size_t nName = strlen(reinterpret_cast<const char *>(pName));
|
|
|
|
// 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
|
|
//
|
|
if ( reinterpret_cast<const UTF8 *>(strchr(reinterpret_cast<const char *>("*!#"), pName[0]))
|
|
|| mux_isspace(pName[0])
|
|
|| mux_isspace(pName[nName-1]))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
|
|
// and OR_TOKEN are allowed.
|
|
//
|
|
if ( mudstate.bStandAlone
|
|
|| mudconf.name_spaces)
|
|
{
|
|
const UTF8 *p = pName;
|
|
while ('\0' != *p)
|
|
{
|
|
if ( !mux_isplayername(p)
|
|
&& ' ' != *p)
|
|
{
|
|
return false;
|
|
}
|
|
p = utf8_advance_predicate(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const UTF8 *p = pName;
|
|
while ('\0' != *p)
|
|
{
|
|
if (!mux_isplayername(p))
|
|
{
|
|
return false;
|
|
}
|
|
p = utf8_advance_predicate(p);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (IsRestricted(pName, mudconf.player_name_charset))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ok_password(const UTF8 *password, const UTF8 **pmsg)
|
|
{
|
|
*pmsg = nullptr;
|
|
|
|
if (*password == '\0')
|
|
{
|
|
*pmsg = T("Null passwords are not allowed.");
|
|
return false;
|
|
}
|
|
|
|
int num_upper = 0;
|
|
int num_special = 0;
|
|
int num_lower = 0;
|
|
|
|
const UTF8 *scan = password;
|
|
for ( ; *scan; scan = utf8_advance_predicate(scan))
|
|
{
|
|
if ( !mux_isprint(scan)
|
|
|| mux_isspace(*scan))
|
|
{
|
|
*pmsg = T("Illegal character in password.");
|
|
return false;
|
|
}
|
|
if (mux_isupper_ascii(*scan))
|
|
{
|
|
num_upper++;
|
|
}
|
|
else if (mux_islower_ascii(*scan))
|
|
{
|
|
num_lower++;
|
|
}
|
|
else if ( *scan != '\''
|
|
&& *scan != '-')
|
|
{
|
|
num_special++;
|
|
}
|
|
}
|
|
|
|
if ( !mudstate.bStandAlone
|
|
&& mudconf.safer_passwords)
|
|
{
|
|
if (num_upper < 1)
|
|
{
|
|
*pmsg = T("The password must contain at least one capital letter.");
|
|
return false;
|
|
}
|
|
if (num_lower < 1)
|
|
{
|
|
*pmsg = T("The password must contain at least one lowercase letter.");
|
|
return false;
|
|
}
|
|
if (num_special < 1)
|
|
{
|
|
*pmsg = T("The password must contain at least one number or a symbol other than the apostrophe or dash.");
|
|
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)
|
|
{
|
|
UTF8 *buf = alloc_lbuf("handle_ears");
|
|
UTF8 *bp = buf;
|
|
|
|
// Moniker returns PUA-encoded name.
|
|
//
|
|
const UTF8 *name = Moniker(thing);
|
|
if (isExit(thing))
|
|
{
|
|
// Truncate at first semicolon.
|
|
//
|
|
const UTF8 *semi = (const UTF8 *)strchr((const char *)name, ';');
|
|
if (semi)
|
|
{
|
|
safe_copy_buf(name, semi - name, buf, &bp);
|
|
}
|
|
else
|
|
{
|
|
safe_str(name, buf, &bp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safe_str(name, buf, &bp);
|
|
}
|
|
|
|
const PRONOUN_SET *pg = get_pronoun_set(thing);
|
|
|
|
if (can_hear)
|
|
{
|
|
safe_tprintf_str(buf, &bp, T(" grow%s ears and can now hear."),
|
|
pg->plural ? "" : "s");
|
|
}
|
|
else
|
|
{
|
|
safe_tprintf_str(buf, &bp, T(" lose%s %s ears and become%s deaf."),
|
|
pg->plural ? "" : "s", pg->possessive,
|
|
pg->plural ? "" : "s");
|
|
}
|
|
*bp = '\0';
|
|
notify_check(thing, thing, buf, MSG_ME | MSG_NBR | MSG_LOC | MSG_INV);
|
|
free_lbuf(buf);
|
|
}
|
|
}
|
|
|
|
// For lack of better place the @switch code is here.
|
|
//
|
|
void do_switch
|
|
(
|
|
dbref executor, dbref caller, dbref enactor,
|
|
int eval, int key,
|
|
UTF8 *expr,
|
|
UTF8 *args[], int nargs,
|
|
const UTF8 *cargs[], int ncargs
|
|
)
|
|
{
|
|
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;
|
|
UTF8 *buff, *bp;
|
|
buff = bp = alloc_lbuf("do_switch");
|
|
CLinearTimeAbsolute lta;
|
|
for ( a = 0;
|
|
( !bMatchOne
|
|
|| !bAny)
|
|
&& a < nargs - 1
|
|
&& args[a]
|
|
&& args[a + 1];
|
|
a += 2)
|
|
{
|
|
bp = buff;
|
|
mux_exec(args[a], LBUF_SIZE-1, buff, &bp, executor, caller, enactor, eval|EV_FCHECK|EV_EVAL|EV_TOP,
|
|
cargs, ncargs);
|
|
*bp = '\0';
|
|
if (wild_match(buff, expr))
|
|
{
|
|
const UTF8 *save_switch = mudstate.switch_token;
|
|
mudstate.switch_token = expr;
|
|
if (key & SWITCH_NOW)
|
|
{
|
|
process_command(executor, caller, enactor, eval, false, args[a+1], cargs, ncargs);
|
|
}
|
|
else
|
|
{
|
|
{
|
|
// Preserve enclosing iter context (## from @dolist)
|
|
// so it survives into the queued @switch body.
|
|
// Respect safer_iter — same guard as @dolist uses.
|
|
//
|
|
const UTF8 *iter_tok = nullptr;
|
|
int iter_num = 0;
|
|
if (!mudconf.safer_iter)
|
|
{
|
|
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];
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
mudstate.switch_token = save_switch;
|
|
bAny = true;
|
|
}
|
|
}
|
|
|
|
free_lbuf(buff);
|
|
if ( a < nargs
|
|
&& !bAny
|
|
&& args[a])
|
|
{
|
|
const UTF8 *save_switch = mudstate.switch_token;
|
|
mudstate.switch_token = expr;
|
|
if (key & SWITCH_NOW)
|
|
{
|
|
process_command(executor, caller, enactor, eval, false, args[a], cargs, ncargs);
|
|
}
|
|
else
|
|
{
|
|
{
|
|
const UTF8 *iter_tok = nullptr;
|
|
int iter_num = 0;
|
|
if (!mudconf.safer_iter)
|
|
{
|
|
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];
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
mudstate.switch_token = save_switch;
|
|
}
|
|
|
|
if (key & SWITCH_NOTIFY)
|
|
{
|
|
UTF8 *tbuf = alloc_lbuf("switch.notify_cmd");
|
|
mux_strncpy(tbuf, T("@notify/quiet me"), LBUF_SIZE-1);
|
|
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, A_SEMAPHORE,
|
|
tbuf,
|
|
ncargs, cargs,
|
|
mudstate.global_regs);
|
|
free_lbuf(tbuf);
|
|
}
|
|
}
|
|
|
|
// Also for lack of better place the @ifelse code is here.
|
|
// Idea for @ifelse from ChaoticMUX.
|
|
//
|
|
void do_if
|
|
(
|
|
dbref player, dbref caller, dbref enactor,
|
|
int eval, int key,
|
|
UTF8 *expr,
|
|
UTF8 *args[], int nargs,
|
|
const UTF8 *cargs[], int ncargs
|
|
)
|
|
{
|
|
UNUSED_PARAMETER(key);
|
|
|
|
if ( !expr
|
|
|| nargs <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UTF8 *buff, *bp;
|
|
CLinearTimeAbsolute lta;
|
|
buff = bp = alloc_lbuf("do_if");
|
|
|
|
mux_exec(expr, LBUF_SIZE-1, buff, &bp, player, caller, enactor, eval|EV_FCHECK|EV_EVAL|EV_TOP,
|
|
cargs, ncargs);
|
|
*bp = '\0';
|
|
|
|
int a = !xlate(buff);
|
|
free_lbuf(buff);
|
|
|
|
if (a < nargs)
|
|
{
|
|
wait_que(player, caller, enactor, eval, false, lta, NOTHING, 0,
|
|
args[a],
|
|
ncargs, cargs,
|
|
mudstate.global_regs);
|
|
}
|
|
}
|
|
|
|
void do_addcommand
|
|
(
|
|
dbref player,
|
|
dbref caller,
|
|
dbref enactor,
|
|
int eval,
|
|
int key,
|
|
int nargs,
|
|
UTF8 *name,
|
|
UTF8 *command,
|
|
const UTF8 *cargs[],
|
|
int ncargs
|
|
)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bNoEval = (key & ADDCMD_NOEVAL) != 0;
|
|
|
|
// Validate command name.
|
|
//
|
|
static UTF8 pName[LBUF_SIZE];
|
|
if (1 <= nargs)
|
|
{
|
|
// 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';
|
|
}
|
|
{
|
|
size_t nPlain = strlen(reinterpret_cast<const char *>(pName));
|
|
UTF8 tmp[LBUF_SIZE];
|
|
nPlain = co_tolower(tmp, pName, nPlain);
|
|
memcpy(pName, tmp, nPlain + 1);
|
|
}
|
|
}
|
|
if ( 0 == nargs
|
|
|| '\0' == pName[0]
|
|
|| ( pName[0] == '_'
|
|
&& pName[1] == '_'))
|
|
{
|
|
notify(player, T("That is not a valid command name."));
|
|
return;
|
|
}
|
|
|
|
// Validate object/attribute.
|
|
//
|
|
dbref thing;
|
|
ATTR *pattr;
|
|
if ( !parse_attrib(player, command, &thing, &pattr)
|
|
|| !pattr)
|
|
{
|
|
notify(player, T("No such attribute."));
|
|
return;
|
|
}
|
|
if (!See_attr(player, thing, pattr))
|
|
{
|
|
notify(player, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
|
|
size_t nName = strlen(reinterpret_cast<char *>(pName));
|
|
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;
|
|
|
|
CMDENT *cmd;
|
|
|
|
if ( old
|
|
&& (old->callseq & CS_ADDED))
|
|
{
|
|
// Don't allow the same (thing,atr) in the list.
|
|
//
|
|
for (auto &entry : *old->addent)
|
|
{
|
|
if ( entry.thing == thing
|
|
&& entry.atr == pattr->number)
|
|
{
|
|
notify(player, tprintf(T("%s already added."), pName));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise, add another (thing,atr) to the list.
|
|
//
|
|
old->addent->push_back(ADDENT{});
|
|
ADDENT &add = old->addent->back();
|
|
add.thing = thing;
|
|
add.atr = pattr->number;
|
|
add.name = reinterpret_cast<const char *>(pName);
|
|
if (bNoEval)
|
|
{
|
|
old->callseq |= CS_NOINTERP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (old)
|
|
{
|
|
// Delete the old built-in (which will later be added back as
|
|
// __name).
|
|
//
|
|
mudstate.command_htab.erase(std::vector<UTF8>(pName, pName + nName));
|
|
}
|
|
|
|
cmd = nullptr;
|
|
try
|
|
{
|
|
cmd = new CMDENT;
|
|
}
|
|
catch (...)
|
|
{
|
|
; // Nothing.
|
|
}
|
|
ISOUTOFMEMORY(cmd);
|
|
cmd->cmdname = StringClone(pName);
|
|
cmd->switches = nullptr;
|
|
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;
|
|
}
|
|
if (bNoEval)
|
|
{
|
|
cmd->callseq |= CS_NOINTERP;
|
|
}
|
|
cmd->flags = CEF_ALLOC;
|
|
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);
|
|
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pName, pName + nName), cmd);
|
|
|
|
if ( old
|
|
&& strcmp(reinterpret_cast<char *>(pName), reinterpret_cast<char *>(old->cmdname)) == 0)
|
|
{
|
|
// We are @addcommand'ing over a built-in command by its
|
|
// unaliased name, therefore, we want to re-target all the
|
|
// aliases.
|
|
//
|
|
UTF8 *p = tprintf(T("__%s"), pName);
|
|
size_t nP = strlen(reinterpret_cast<char *>(p));
|
|
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);
|
|
}
|
|
}
|
|
|
|
// We reset the one letter commands here so you can overload them.
|
|
//
|
|
cache_prefix_cmds();
|
|
notify(player, tprintf(T("Command %s added."), pName));
|
|
}
|
|
|
|
void do_listcommands(dbref player, dbref caller, dbref enactor, int eval,
|
|
int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
CMDENT *old;
|
|
bool didit = false;
|
|
|
|
// Let's make this case insensitive...
|
|
//
|
|
size_t nCased;
|
|
UTF8 *pCased = mux_strlwr(name, nCased);
|
|
|
|
if (*pCased)
|
|
{
|
|
{
|
|
auto it = mudstate.command_htab.find(std::vector<UTF8>(pCased, pCased + nCased));
|
|
old = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
|
|
}
|
|
|
|
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...
|
|
//
|
|
for (auto &entry : *old->addent)
|
|
{
|
|
ATTR *ap = reinterpret_cast<ATTR *>(atr_num(entry.atr));
|
|
const UTF8 *pName = T("(WARNING: Bad Attribute Number)");
|
|
if (ap)
|
|
{
|
|
pName = ap->name;
|
|
}
|
|
notify(player, tprintf(T("%s: #%d/%s"), reinterpret_cast<const UTF8 *>(entry.name.c_str()), entry.thing, pName));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
notify(player, tprintf(T("%s not found in command table."), pCased));
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for (auto &[key, val] : mudstate.command_htab)
|
|
{
|
|
old = static_cast<CMDENT*>(val);
|
|
if (old->callseq & CS_ADDED)
|
|
{
|
|
const UTF8 *pKeyName = key.data();
|
|
int nKeyName = static_cast<int>(key.size());
|
|
for (auto &entry : *old->addent)
|
|
{
|
|
if ( static_cast<size_t>(nKeyName) != entry.name.size()
|
|
|| memcmp(pKeyName, entry.name.c_str(), nKeyName) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
ATTR *ap = reinterpret_cast<ATTR *>(atr_num(entry.atr));
|
|
const UTF8 *pName = T("(WARNING: Bad Attribute Number)");
|
|
if (ap)
|
|
{
|
|
pName = ap->name;
|
|
}
|
|
notify(player, tprintf(T("%s: #%d/%s"), reinterpret_cast<const UTF8 *>(entry.name.c_str()),
|
|
entry.thing, pName));
|
|
didit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!didit)
|
|
{
|
|
notify(player, T("No added commands found in command table."));
|
|
}
|
|
}
|
|
|
|
void do_delcommand
|
|
(
|
|
dbref player,
|
|
dbref caller,
|
|
dbref enactor,
|
|
int eval,
|
|
int key,
|
|
int nargs,
|
|
UTF8 *name,
|
|
UTF8 *command,
|
|
const UTF8 *cargs[],
|
|
int ncargs
|
|
)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(nargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if (!*name)
|
|
{
|
|
notify(player, T("Sorry."));
|
|
return;
|
|
}
|
|
|
|
dbref thing = NOTHING;
|
|
int atr = NOTHING;
|
|
ATTR *pattr;
|
|
if (*command)
|
|
{
|
|
if ( !parse_attrib(player, command, &thing, &pattr)
|
|
|| !pattr)
|
|
{
|
|
notify(player, T("No such attribute."));
|
|
return;
|
|
}
|
|
if (!See_attr(player, thing, pattr))
|
|
{
|
|
notify(player, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
atr = pattr->number;
|
|
}
|
|
|
|
// Let's make this case insensitive...
|
|
//
|
|
size_t nCased;
|
|
UTF8 *pCased = mux_strlwr(name, nCased);
|
|
|
|
CMDENT *old, *cmd;
|
|
{
|
|
auto it = mudstate.command_htab.find(std::vector<UTF8>(pCased, pCased + nCased));
|
|
old = (it != mudstate.command_htab.end()) ? static_cast<CMDENT*>(it->second) : nullptr;
|
|
}
|
|
|
|
if ( old
|
|
&& (old->callseq & CS_ADDED))
|
|
{
|
|
UTF8 *p__Name = tprintf(T("__%s"), pCased);
|
|
size_t n__Name = strlen(reinterpret_cast<char *>(p__Name));
|
|
|
|
if (command[0] == '\0')
|
|
{
|
|
// Delete all @addcommand'ed associations with the given name.
|
|
//
|
|
delete old->addent;
|
|
old->addent = nullptr;
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pCased, pCased + nCased), cmd);
|
|
}
|
|
|
|
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; }
|
|
}
|
|
else
|
|
{
|
|
// TODO: Delete everything related to 'old'.
|
|
//
|
|
}
|
|
MEMFREE(old->cmdname);
|
|
old->cmdname = nullptr;
|
|
MEMFREE(old);
|
|
old = nullptr;
|
|
cache_prefix_cmds();
|
|
notify(player, T("Done."));
|
|
}
|
|
else
|
|
{
|
|
// Remove only the (name,thing,atr) association.
|
|
//
|
|
for (auto it = old->addent->begin(); it != old->addent->end(); ++it)
|
|
{
|
|
if ( it->thing == thing
|
|
&& it->atr == atr)
|
|
{
|
|
old->addent->erase(it);
|
|
if (old->addent->empty())
|
|
{
|
|
delete old->addent;
|
|
old->addent = nullptr;
|
|
mudstate.command_htab.erase(std::vector<UTF8>(pCased, pCased + nCased));
|
|
{
|
|
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)
|
|
{
|
|
mudstate.command_htab.emplace(std::vector<UTF8>(pCased, pCased + nCased),
|
|
cmd);
|
|
}
|
|
|
|
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; }
|
|
}
|
|
MEMFREE(old->cmdname);
|
|
old->cmdname = nullptr;
|
|
MEMFREE(old);
|
|
old = nullptr;
|
|
}
|
|
cache_prefix_cmds();
|
|
notify(player, T("Done."));
|
|
return;
|
|
}
|
|
}
|
|
notify(player, T("Command not found in command table."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
notify(player, T("Command not found in command table."));
|
|
}
|
|
}
|
|
|
|
void do_quitprog(dbref player, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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))
|
|
{
|
|
notify(player, T("That is not a player."));
|
|
return;
|
|
}
|
|
if (!Connected(doer))
|
|
{
|
|
notify(player, T("That player is not connected."));
|
|
return;
|
|
}
|
|
if (!player_has_program(doer))
|
|
{
|
|
notify(player, T("Player is not in an @program."));
|
|
return;
|
|
}
|
|
|
|
program_data* program = detach_player_program(doer);
|
|
if (program)
|
|
{
|
|
for (auto& wait_reg : program->wait_regs)
|
|
{
|
|
if (wait_reg)
|
|
{
|
|
RegRelease(wait_reg);
|
|
wait_reg = nullptr;
|
|
}
|
|
}
|
|
NamedRegsClear(program->named_wait_regs);
|
|
MEMFREE(program);
|
|
program = nullptr;
|
|
}
|
|
atr_clr(doer, A_PROGCMD);
|
|
notify(player, T("@program cleared."));
|
|
notify(doer, T("Your @program has been terminated."));
|
|
}
|
|
|
|
void do_prog
|
|
(
|
|
dbref player,
|
|
dbref caller,
|
|
dbref enactor,
|
|
int eval,
|
|
int key,
|
|
int nargs,
|
|
UTF8 *name,
|
|
UTF8 *command,
|
|
const UTF8 *cargs[],
|
|
int ncargs
|
|
)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(nargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if ( !name
|
|
|| !*name)
|
|
{
|
|
notify(player, T("No players specified."));
|
|
return;
|
|
}
|
|
|
|
const dbref doer = match_thing(player, name);
|
|
if ( !( Prog(player)
|
|
|| Prog(Owner(player)))
|
|
&& player != doer)
|
|
{
|
|
notify(player, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
if ( !Good_obj(doer)
|
|
|| !isPlayer(doer))
|
|
{
|
|
notify(player, T("That is not a player."));
|
|
return;
|
|
}
|
|
if (!Connected(doer))
|
|
{
|
|
notify(player, T("That player is not connected."));
|
|
return;
|
|
}
|
|
|
|
// Check to see if the enactor already has an @prog input pending.
|
|
//
|
|
if (player_has_program(doer))
|
|
{
|
|
notify(player, T("Input already pending."));
|
|
return;
|
|
}
|
|
|
|
UTF8 *msg = command;
|
|
const UTF8 *attrib = parse_to(&msg, ':', 1);
|
|
|
|
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;
|
|
UTF8 *pBuffer = nullptr;
|
|
bool bFound = false;
|
|
ITER_PARENTS(thing, parent, lev)
|
|
{
|
|
pBuffer = atr_get("do_prog.1405", parent, ap->number, &aowner, &aflags);
|
|
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
|
|
{
|
|
notify(player, T("Attribute not present on object."));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
notify(player, T("No such attribute."));
|
|
return;
|
|
}
|
|
|
|
const auto program = static_cast<program_data*>(MEMALLOC(sizeof(program_data)));
|
|
ISOUTOFMEMORY(program);
|
|
program->wait_enactor = player;
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
{
|
|
program->wait_regs[i] = mudstate.global_regs[i];
|
|
if (mudstate.global_regs[i])
|
|
{
|
|
RegAddRef(mudstate.global_regs[i]);
|
|
}
|
|
}
|
|
program->named_wait_regs = NamedRegsCopy(mudstate.named_regs);
|
|
|
|
// Now, start waiting.
|
|
//
|
|
set_player_program(doer, program);
|
|
send_prog_prompt(doer);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* do_restart: Restarts the game.
|
|
*/
|
|
void do_restart(dbref executor, dbref caller, dbref enactor, int eval, int key)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
|
|
if (!Can_SiteAdmin(executor))
|
|
{
|
|
notify(executor, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
|
|
bool bDenied = false;
|
|
#if defined(HAVE_WORKING_FORK)
|
|
if (mudstate.dumping)
|
|
{
|
|
notify(executor, T("Dumping. Please try again later."));
|
|
bDenied = true;
|
|
}
|
|
#endif // HAVE_WORKING_FORK
|
|
|
|
|
|
if (!mudstate.bCanRestart)
|
|
{
|
|
notify(executor, T("Server just started. Please try again in a few seconds."));
|
|
bDenied = true;
|
|
}
|
|
if (bDenied)
|
|
{
|
|
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
|
|
log_text(T("Restart requested but not executed by "));
|
|
log_name(executor);
|
|
ENDLOG;
|
|
return;
|
|
}
|
|
|
|
#ifdef UNIX_SSL
|
|
raw_broadcast(0, T("GAME: Restart by %s, please wait. (All SSL connections will be dropped.)"), Moniker(Owner(executor)));
|
|
#else
|
|
raw_broadcast(0, T("GAME: Restart by %s, please wait."), Moniker(Owner(executor)));
|
|
#endif
|
|
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
|
|
log_text(T("Restart by "));
|
|
log_name(executor);
|
|
ENDLOG;
|
|
|
|
g_GanlAdapter.prepare_for_restart();
|
|
|
|
local_presync_database();
|
|
ServerEventsSinkNode *p = g_pServerEventsSinkListHead;
|
|
while (nullptr != p)
|
|
{
|
|
p->pSink->presync_database();
|
|
p = p->pNext;
|
|
}
|
|
#if defined(STUB_SLAVE)
|
|
final_stubslave();
|
|
#endif // STUB_SLAVE
|
|
final_modules();
|
|
|
|
pcache_sync();
|
|
dump_database_internal(DUMP_I_RESTART);
|
|
SYNC;
|
|
CLOSE;
|
|
|
|
#if defined(WINDOWS_PROCESSES)
|
|
exit(12345678);
|
|
#elif defined(UNIX_PROCESSES)
|
|
#if defined(HAVE_WORKING_FORK)
|
|
dump_restart_db();
|
|
#endif // HAVE_WORKING_FORK
|
|
|
|
Log.StopLogging();
|
|
|
|
#ifdef GAME_DOOFERMUX
|
|
execl("bin/netmux", mudconf.mud_name, "-c", mudconf.config_file, "-p",
|
|
mudconf.pid_file, "-e", mudconf.log_dir, static_cast<char *>(nullptr));
|
|
#else
|
|
execl("bin/netmux", "netmux", "-c", mudconf.config_file, "-p",
|
|
mudconf.pid_file, "-e", mudconf.log_dir, static_cast<char *>(nullptr));
|
|
#endif // GAME_DOOFERMUX
|
|
mux_assert(false);
|
|
#endif // UNIX_PROCESSES
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* do_comment: Implement the @@ (comment) command. Very cpu-intensive :-)
|
|
*/
|
|
|
|
void do_comment(dbref executor, dbref caller, dbref enactor, int eval, int key)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
}
|
|
|
|
void do_eval(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *arg1, const UTF8 *cargs[], int ncargs)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(arg1);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
}
|
|
|
|
static dbref promote_dflt(dbref old, dbref new0)
|
|
{
|
|
if ( old == NOPERM
|
|
|| new0 == NOPERM)
|
|
{
|
|
return NOPERM;
|
|
}
|
|
if ( old == AMBIGUOUS
|
|
|| new0 == AMBIGUOUS)
|
|
{
|
|
return AMBIGUOUS;
|
|
}
|
|
return NOTHING;
|
|
}
|
|
|
|
dbref match_possessed(dbref player, dbref thing, UTF8 *target, dbref dflt, bool check_enter)
|
|
{
|
|
// First, check normally.
|
|
//
|
|
if (Good_obj(dflt))
|
|
{
|
|
return dflt;
|
|
}
|
|
|
|
// Didn't find it directly. Recursively do a contents check.
|
|
//
|
|
dbref result, result1;
|
|
UTF8 *buff, *place, *s1, *d1, *temp;
|
|
UTF8 *start = target;
|
|
while (*target)
|
|
{
|
|
// Fail if no ' characters.
|
|
//
|
|
place = target;
|
|
target = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(place), '\''));
|
|
if ( target == nullptr
|
|
|| !*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.
|
|
//
|
|
buff = alloc_lbuf("is_posess");
|
|
for (s1 = start, d1 = buff; *s1 && (s1 < temp); *d1++ = (*s1++))
|
|
{
|
|
; // Nothing.
|
|
}
|
|
*d1 = '\0';
|
|
|
|
// 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();
|
|
|
|
free_lbuf(buff);
|
|
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
|
|
*/
|
|
|
|
void parse_range(UTF8 **name, dbref *low_bound, dbref *high_bound)
|
|
{
|
|
UTF8 *buff1 = *name;
|
|
if (buff1 && *buff1)
|
|
{
|
|
*name = parse_to(&buff1, ',', EV_STRIP_TS);
|
|
}
|
|
if (buff1 && *buff1)
|
|
{
|
|
UTF8 *buff2 = parse_to(&buff1, ',', EV_STRIP_TS);
|
|
if (buff1 && *buff1)
|
|
{
|
|
while (mux_isspace(*buff1))
|
|
{
|
|
buff1++;
|
|
}
|
|
|
|
if (*buff1 == NUMBER_TOKEN)
|
|
{
|
|
buff1++;
|
|
}
|
|
|
|
*high_bound = mux_atol(buff1);
|
|
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++;
|
|
}
|
|
|
|
*low_bound = mux_atol(buff2);
|
|
if (*low_bound < 0)
|
|
{
|
|
*low_bound = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*low_bound = 0;
|
|
*high_bound = mudstate.db_top - 1;
|
|
}
|
|
}
|
|
|
|
bool parse_thing_slash(dbref player, const UTF8 *thing, const UTF8 **after, dbref *it)
|
|
{
|
|
// Get name up to '/'.
|
|
//
|
|
size_t i = 0;
|
|
while ( thing[i] != '\0'
|
|
&& thing[i] != '/')
|
|
{
|
|
i++;
|
|
}
|
|
|
|
// If no '/' in string, return failure.
|
|
//
|
|
if (thing[i] == '\0')
|
|
{
|
|
*after = nullptr;
|
|
*it = NOTHING;
|
|
return false;
|
|
}
|
|
*after = thing + i + 1;
|
|
|
|
// Look for the object.
|
|
//
|
|
init_match(player, thing, i, NOTYPE);
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
*it = match_result();
|
|
|
|
// Return status of search.
|
|
//
|
|
return Good_obj(*it);
|
|
}
|
|
|
|
bool get_obj_and_lock(dbref player, const UTF8 *what, dbref *it, ATTR **attr, UTF8 *errmsg, UTF8 **bufc)
|
|
{
|
|
// Get name up to '/'.
|
|
//
|
|
size_t i = 0;
|
|
while ( what[i] != '\0'
|
|
&& what[i] != '/')
|
|
{
|
|
i++;
|
|
}
|
|
|
|
*it = match_thing_quiet(player, what, i);
|
|
if (!Good_obj(*it))
|
|
{
|
|
safe_match_result(*it, errmsg, bufc);
|
|
return false;
|
|
}
|
|
|
|
int anum;
|
|
if (what[i] == '/')
|
|
{
|
|
// <obj>/<lock> syntax, use the named lock.
|
|
//
|
|
if (!search_nametab(player, lock_sw, what + i + 1, &anum))
|
|
{
|
|
safe_str(T("#-1 LOCK NOT FOUND"), errmsg, bufc);
|
|
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);
|
|
if (nullptr == *attr)
|
|
{
|
|
safe_str(T("#-1 LOCK NOT FOUND"), errmsg, bufc);
|
|
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);
|
|
}
|
|
|
|
int test_flags = tattr->flags;
|
|
|
|
if (mudstate.attrperm_list)
|
|
{
|
|
ATTRPERM *perm_walk = mudstate.attrperm_list;
|
|
while (nullptr != perm_walk)
|
|
{
|
|
if (quick_wild(perm_walk->wildcard, tattr->name))
|
|
{
|
|
test_flags |= perm_walk->flags;
|
|
}
|
|
perm_walk = perm_walk->next;
|
|
}
|
|
}
|
|
|
|
int mAllow = AF_VISUAL;
|
|
if ( (test_flags & mAllow)
|
|
|| (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)
|
|
{
|
|
if ( (test_flags & mDeny)
|
|
|| (aflags & mDeny))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool bCanSetAttr(dbref executor, dbref target, ATTR *tattr)
|
|
{
|
|
if (!tattr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (NoModify(target) && !WizRoy(executor))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
while (nullptr != perm_walk)
|
|
{
|
|
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)))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool bCanLockAttr(dbref executor, dbref target, ATTR *tattr)
|
|
{
|
|
if (!tattr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (NoModify(target) && !WizRoy(executor))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
while (nullptr != perm_walk)
|
|
{
|
|
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))
|
|
{
|
|
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)
|
|
{
|
|
// No sense in trying to locate a bad object.
|
|
//
|
|
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
|
|
// examine the location, if the player is a wizard, or if the target
|
|
// caused the lookup.
|
|
//
|
|
if ( Examinable(player, it)
|
|
|| Find_Unfindable(player)
|
|
|| loc_it == player
|
|
|| ( loc_it != NOTHING
|
|
&& ( Examinable(player, loc_it)
|
|
|| loc_it == where_is(player))
|
|
&& ( !Hidden(it)
|
|
|| See_Hidden(player)))
|
|
|| Wizard(player)
|
|
|| 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))
|
|
&& ( !Hidden(it)
|
|
|| See_Hidden(player))
|
|
|| Find_Unfindable(player)
|
|
|| ( Findable(it)
|
|
&& findable_room)
|
|
&& ( !Hidden(it)
|
|
|| See_Hidden(player)))
|
|
{
|
|
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)
|
|
{
|
|
if (!IsReal(player, exit))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif // REALITY_LVLS
|
|
|
|
// 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;
|
|
}
|
|
|
|
// VisibleLock
|
|
//
|
|
if (!could_doit(player, exit, A_LVISIBLE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 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
|
|
|
|
#ifdef REALITY_LVLS
|
|
if (!mudstate.bStandAlone)
|
|
{
|
|
if (!IsReal(player, exit))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif // REALITY_LVLS
|
|
|
|
// Light exit
|
|
//
|
|
if (Light(exit))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Dark location or base.
|
|
//
|
|
if (key & (VE_LOC_DARK | VE_BASE_DARK))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// VisibleLock
|
|
//
|
|
if (!could_doit(player, exit, A_LVISIBLE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Default
|
|
//
|
|
return true;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* did_it: Have player do something to/with thing
|
|
*/
|
|
|
|
void did_it(dbref player, dbref thing, int what, const UTF8 *def, int owhat,
|
|
const UTF8 *odef, int awhat, int ctrl_flags,
|
|
const UTF8 *args[], int nargs)
|
|
{
|
|
if (alarm_clock.alarmed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UTF8 *d, *buff, *act, *charges, *bp;
|
|
dbref loc, aowner;
|
|
int num, aflags;
|
|
|
|
// 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;
|
|
reg_ref **preserve = nullptr;
|
|
|
|
// message to player.
|
|
//
|
|
if (what > 0)
|
|
{
|
|
d = atr_pget(thing, what, &aowner, &aflags);
|
|
if (*d)
|
|
{
|
|
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
|
{
|
|
// Output raw text with no substitutions.
|
|
//
|
|
notify(player, d);
|
|
}
|
|
else
|
|
{
|
|
need_pres = true;
|
|
preserve = PushRegisters(MAX_GLOBAL_REGS);
|
|
save_global_regs(preserve);
|
|
|
|
buff = bp = alloc_lbuf("did_it.1");
|
|
mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, player, player,
|
|
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);
|
|
}
|
|
}
|
|
else if (def)
|
|
{
|
|
notify(player, def);
|
|
}
|
|
free_lbuf(d);
|
|
}
|
|
else if (what < 0 && def)
|
|
{
|
|
notify(player, def);
|
|
}
|
|
|
|
// message to neighbors.
|
|
//
|
|
if ( 0 < owhat
|
|
&& Has_location(player)
|
|
&& Good_obj(loc = Location(player)))
|
|
{
|
|
d = atr_pget(thing, owhat, &aowner, &aflags);
|
|
if (*d)
|
|
{
|
|
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
|
|
{
|
|
if (!need_pres)
|
|
{
|
|
need_pres = true;
|
|
preserve = PushRegisters(MAX_GLOBAL_REGS);
|
|
save_global_regs(preserve);
|
|
}
|
|
buff = bp = alloc_lbuf("did_it.2");
|
|
mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, player, player,
|
|
AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_FCHECK|EV_TOP),
|
|
args, nargs);
|
|
*bp = '\0';
|
|
if (*buff)
|
|
{
|
|
#ifdef REALITY_LVLS
|
|
if (aflags & AF_NONAME)
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing, buff);
|
|
}
|
|
else
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing,
|
|
tprintf(T("%s %s"), Moniker(player), buff));
|
|
}
|
|
#else
|
|
if (aflags & AF_NONAME)
|
|
{
|
|
notify_except2(loc, player, player, thing, buff);
|
|
}
|
|
else
|
|
{
|
|
notify_except2(loc, player, player, thing,
|
|
tprintf(T("%s %s"), Moniker(player), buff));
|
|
}
|
|
#endif // REALITY_LVLS
|
|
}
|
|
free_lbuf(buff);
|
|
} // else (not NOEVAL)
|
|
}
|
|
else if (odef)
|
|
{
|
|
#ifdef REALITY_LVLS
|
|
if (ctrl_flags & VERB_NONAME)
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing, odef);
|
|
}
|
|
else
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing,
|
|
tprintf(T("%s %s"), Moniker(player), odef));
|
|
}
|
|
#else
|
|
if (ctrl_flags & VERB_NONAME)
|
|
{
|
|
notify_except2(loc, player, player, thing, odef);
|
|
}
|
|
else
|
|
{
|
|
notify_except2(loc, player, player, thing,
|
|
tprintf(T("%s %s"), Moniker(player), odef));
|
|
}
|
|
#endif // REALITY_LVLS
|
|
}
|
|
free_lbuf(d);
|
|
} else if ( owhat < 0
|
|
&& odef
|
|
&& Has_location(player)
|
|
&& Good_obj(loc = Location(player)))
|
|
{
|
|
#ifdef REALITY_LVLS
|
|
if (ctrl_flags & VERB_NONAME)
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing, odef);
|
|
}
|
|
else
|
|
{
|
|
notify_except2_rlevel(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef));
|
|
}
|
|
#else
|
|
if (ctrl_flags & VERB_NONAME)
|
|
{
|
|
notify_except2(loc, player, player, thing, odef);
|
|
}
|
|
else
|
|
{
|
|
notify_except2(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef));
|
|
}
|
|
#endif // REALITY_LVLS
|
|
}
|
|
|
|
// If we preserved the state of the global registers, restore them.
|
|
//
|
|
if (need_pres)
|
|
{
|
|
restore_global_regs(preserve);
|
|
PopRegisters(preserve, MAX_GLOBAL_REGS);
|
|
}
|
|
|
|
// Do the action attribute.
|
|
//
|
|
#ifdef REALITY_LVLS
|
|
if ( 0 < awhat
|
|
&& IsReal(thing, player))
|
|
#else
|
|
if (0 < awhat)
|
|
#endif // REALITY_LVLS
|
|
{
|
|
if (*(act = atr_pget(thing, awhat, &aowner, &aflags)))
|
|
{
|
|
dbref aowner2;
|
|
int aflags2;
|
|
charges = atr_pget(thing, A_CHARGES, &aowner2, &aflags2);
|
|
if (*charges)
|
|
{
|
|
num = mux_atol(charges);
|
|
if (num > 0)
|
|
{
|
|
buff = alloc_sbuf("did_it.charges");
|
|
mux_ltoa(num-1, buff);
|
|
atr_add_raw(thing, A_CHARGES, buff);
|
|
free_sbuf(buff);
|
|
}
|
|
else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner2, &aflags2)))
|
|
{
|
|
free_lbuf(act);
|
|
act = buff;
|
|
}
|
|
else
|
|
{
|
|
free_lbuf(act);
|
|
free_lbuf(buff);
|
|
free_lbuf(charges);
|
|
return;
|
|
}
|
|
}
|
|
free_lbuf(charges);
|
|
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);
|
|
}
|
|
}
|
|
free_lbuf(act);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* do_verb: Command interface to did_it.
|
|
*/
|
|
|
|
void do_verb(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
|
UTF8 *victim_str, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
|
{
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(key);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// Look for the victim.
|
|
//
|
|
if ( !victim_str
|
|
|| !*victim_str)
|
|
{
|
|
notify(executor, T("Nothing to do."));
|
|
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))
|
|
{
|
|
notify_quiet(executor, T("Permission denied,"));
|
|
return;
|
|
}
|
|
|
|
ATTR *ap;
|
|
int what = -1;
|
|
int owhat = -1;
|
|
int awhat = -1;
|
|
const UTF8 *whatd = nullptr;
|
|
const UTF8 *owhatd = nullptr;
|
|
int nxargs = 0;
|
|
dbref aowner = NOTHING;
|
|
int aflags = NOTHING;
|
|
UTF8 *xargs[10];
|
|
|
|
switch (nargs) // Yes, this IS supposed to fall through.
|
|
{
|
|
case 7:
|
|
// Get arguments.
|
|
//
|
|
parse_arglist(victim, actor, actor, args[6],
|
|
EV_STRIP_LS | EV_STRIP_TS, xargs, 10, nullptr, 0, &nxargs);
|
|
|
|
case 6:
|
|
// Get action attribute.
|
|
//
|
|
ap = atr_str(args[5]);
|
|
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.
|
|
//
|
|
ap = atr_str(args[3]);
|
|
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.
|
|
//
|
|
ap = atr_str(args[1]);
|
|
if (ap && (ap->number > 0))
|
|
{
|
|
what = ap->number;
|
|
}
|
|
}
|
|
|
|
// If executor doesn't control both, enforce visibility restrictions.
|
|
//
|
|
if (!Controls(executor, victim))
|
|
{
|
|
ap = nullptr;
|
|
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;
|
|
}
|
|
|
|
ap = nullptr;
|
|
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.
|
|
//
|
|
did_it(actor, victim, what, whatd, owhat, owhatd, awhat,
|
|
key & VERB_NONAME, (const UTF8 **)xargs, nxargs);
|
|
|
|
// Free user args.
|
|
//
|
|
for (int i = 0; i < nxargs; i++)
|
|
{
|
|
free_lbuf(xargs[i]);
|
|
}
|
|
}
|
|
|
|
// AssertionFailed and OutOfMemory moved to libmux.cpp.
|
|
|
|
static void ListReferences(dbref executor, UTF8 *reference_name)
|
|
{
|
|
dbref target = NOTHING;
|
|
bool global_only = false;
|
|
|
|
if ( nullptr == reference_name
|
|
|| '\0' == reference_name[0])
|
|
{
|
|
global_only = true;
|
|
}
|
|
else
|
|
{
|
|
global_only = false;
|
|
target = lookup_player(executor, reference_name, 1);
|
|
if (!Good_obj(target))
|
|
{
|
|
raw_notify(executor, T("No such player."));
|
|
return;
|
|
}
|
|
|
|
if (!Controls(executor, target))
|
|
{
|
|
raw_notify(executor, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Listing:
|
|
// - 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;
|
|
|
|
for (auto &[ref_key, ref_val] : mudstate.reference_htab)
|
|
{
|
|
htab_entry = static_cast<reference_entry*>(ref_val);
|
|
if ( ( global_only
|
|
&& '_' == htab_entry->name[0])
|
|
|| ( !global_only
|
|
&& target == htab_entry->owner))
|
|
{
|
|
if (!Good_obj(htab_entry->target))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!match_found)
|
|
{
|
|
match_found = true;
|
|
raw_notify(executor, tprintf(T("%-12s %-20s %-20s"),
|
|
T("Reference"), T("Target"), T("Owner")));
|
|
raw_notify(executor,
|
|
T("-------------------------------------------------------"));
|
|
}
|
|
|
|
UTF8 *object_buf =
|
|
unparse_object(executor, htab_entry->target, false);
|
|
|
|
raw_notify(executor, tprintf(T("%-12s %-20s %-20s"), htab_entry->name,
|
|
object_buf, Moniker(htab_entry->owner)));
|
|
|
|
free_lbuf(object_buf);
|
|
}
|
|
}
|
|
|
|
if (!match_found)
|
|
{
|
|
raw_notify(executor, T("GAME: No references found."));
|
|
}
|
|
else
|
|
{
|
|
raw_notify(executor,
|
|
T("---------------- End of Reference List ----------------"));
|
|
}
|
|
}
|
|
|
|
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
|
|
)
|
|
{
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
if (key & REFERENCE_LIST)
|
|
{
|
|
ListReferences(executor, reference_name);
|
|
return;
|
|
}
|
|
|
|
// References can only be set on objects the executor can examine.
|
|
//
|
|
dbref target = NOTHING;
|
|
if ( nullptr != object_name
|
|
&& '\0' != object_name[0])
|
|
{
|
|
target = match_thing_quiet(executor, object_name);
|
|
|
|
if (!Good_obj(target))
|
|
{
|
|
notify(executor, NOMATCH_MESSAGE);
|
|
return;
|
|
}
|
|
else if (!Examinable(executor, target))
|
|
{
|
|
notify(executor, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ('_' == reference_name[0])
|
|
{
|
|
if (!Wizard(executor))
|
|
{
|
|
notify(executor, NOPERM_MESSAGE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
UTF8 tbuf[LBUF_SIZE];
|
|
size_t tbuf_len;
|
|
if ('_' == reference_name[0])
|
|
{
|
|
tbuf_len = static_cast<size_t>(
|
|
snprintf(reinterpret_cast<char *>(tbuf), sizeof(tbuf),
|
|
"%s", reinterpret_cast<const char *>(reference_name)));
|
|
}
|
|
else
|
|
{
|
|
tbuf_len = static_cast<size_t>(
|
|
snprintf(reinterpret_cast<char *>(tbuf), sizeof(tbuf),
|
|
"%s.%ld", reinterpret_cast<const char *>(reference_name),
|
|
static_cast<long>(executor)));
|
|
}
|
|
auto it_ref = mudstate.reference_htab.find(std::vector<UTF8>(tbuf, tbuf + tbuf_len));
|
|
struct reference_entry *result = (it_ref != mudstate.reference_htab.end())
|
|
? static_cast<reference_entry*>(it_ref->second) : nullptr;
|
|
|
|
enum { Delete, Add, Update, NotFound, Redundant, OutOfMemory } eOperation;
|
|
|
|
if (nullptr != result)
|
|
{
|
|
if (NOTHING == target)
|
|
{
|
|
eOperation = Delete;
|
|
}
|
|
else if (result->target == target)
|
|
{
|
|
eOperation = Redundant;
|
|
}
|
|
else // if (result->target != target)
|
|
{
|
|
eOperation = Update;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NOTHING == target)
|
|
{
|
|
eOperation = NotFound;
|
|
}
|
|
else
|
|
{
|
|
eOperation = Add;
|
|
if ( !Wizard(executor)
|
|
&& ThrottleReferences(executor))
|
|
{
|
|
raw_notify(executor, T("References requested too quickly."));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Delete == eOperation
|
|
|| Update == eOperation)
|
|
{
|
|
// Release the existing reference.
|
|
//
|
|
MEMFREE(result->name);
|
|
result->name = nullptr;
|
|
MEMFREE(result);
|
|
result = nullptr;
|
|
mudstate.reference_htab.erase(std::vector<UTF8>(tbuf, tbuf + tbuf_len));
|
|
}
|
|
|
|
if ( Update == eOperation
|
|
|| Add == eOperation)
|
|
{
|
|
try
|
|
{
|
|
result = static_cast<reference_entry *>(MEMALLOC(sizeof(reference_entry)));
|
|
}
|
|
catch(...)
|
|
{
|
|
; // Nothing;
|
|
}
|
|
|
|
if (nullptr != result)
|
|
{
|
|
result->target = target;
|
|
result->owner = executor;
|
|
result->name = StringCloneLen(tbuf, tbuf_len);
|
|
mudstate.reference_htab.emplace(std::vector<UTF8>(tbuf, tbuf + tbuf_len), result);
|
|
}
|
|
else
|
|
{
|
|
eOperation = OutOfMemory;
|
|
}
|
|
}
|
|
|
|
if (Delete == eOperation)
|
|
{
|
|
raw_notify(executor, T("Reference cleared."));
|
|
}
|
|
else if (Update == eOperation)
|
|
{
|
|
raw_notify(executor, T("Reference updated."));
|
|
}
|
|
else if (Redundant == eOperation)
|
|
{
|
|
raw_notify(executor, T("That reference already exists."));
|
|
}
|
|
else if (NotFound == eOperation)
|
|
{
|
|
raw_notify(executor, T("No such reference to clear."));
|
|
}
|
|
else if (Add == eOperation)
|
|
{
|
|
raw_notify(executor, T("Reference added."));
|
|
}
|
|
else if (OutOfMemory == eOperation)
|
|
{
|
|
raw_notify(executor, OUT_OF_MEMORY);
|
|
}
|
|
}
|