2007-01-03 03:11:53 +00:00
|
|
|
|
/*! \file speech.cpp
|
|
|
|
|
|
* \brief Commands which involve speaking.
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
#include "copyright.h"
|
|
|
|
|
|
#include "autoconf.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include "externs.h"
|
|
|
|
|
|
|
2022-03-13 14:06:30 -06:00
|
|
|
|
UTF8 *modSpeech(dbref player, const UTF8 *message, bool bWhich, const UTF8 *command)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf mod = LBuf_Adopt(atr_get("modSpeech.25", player, bWhich ? A_SPEECHMOD : A_SAYSTRING,
|
|
|
|
|
|
&aowner, &aflags));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( mod[0] == '\0'
|
2018-06-03 03:27:25 -06:00
|
|
|
|
|| alarm_clock.alarmed)
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *new_message = alloc_lbuf("modspeech");
|
|
|
|
|
|
UTF8 *t_ptr = new_message;
|
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(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
mux_strncpy(new_message, mod, LBUF_SIZE-1);
|
|
|
|
|
|
t_ptr = new_message + strlen((const char *)new_message);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
const UTF8 *args[2];
|
|
|
|
|
|
args[0] = message;
|
|
|
|
|
|
args[1] = command;
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(mod, LBUF_SIZE-1, new_message, &t_ptr, player, 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_FCHECK|EV_EVAL|EV_TOP), args, 2);
|
|
|
|
|
|
}
|
2007-04-14 04:26:38 +00:00
|
|
|
|
*t_ptr = '\0';
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return new_message;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 19:34:54 -06:00
|
|
|
|
static int64_t idle_timeout_val(dbref player)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// If IDLETIMEOUT attribute is not present, the value
|
|
|
|
|
|
// returned will be zero.
|
|
|
|
|
|
//
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// int64_t, not int (#1402): attribute-sourced seconds stay full width.
|
|
|
|
|
|
//
|
2006-09-01 22:59:23 +00:00
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf ITbuffer = LBuf_Adopt(atr_get("idle_timeout_val.53", player, A_IDLETMOUT, &aowner, &aflags));
|
2026-07-28 19:34:54 -06:00
|
|
|
|
return mux_atoi64(ITbuffer);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool sp_ok(dbref player)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Gagged(player)
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Sorry. Gagged players cannot speak."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!mudconf.robot_speak)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Robot(player) && !Controls(player, Location(player)))
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Sorry, robots may not speak in public."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Auditorium(Location(player)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!could_doit(player, Location(player), A_LSPEECH))
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Sorry, you may not speak in this place."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-06 06:17:46 +00:00
|
|
|
|
void do_think(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UTF8 *message, const UTF8 *cargs[], int ncargs)
|
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
|
|
|
|
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
UTF8 *bp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf buf = LBuf_Src("do_think");
|
|
|
|
|
|
bp = buf;
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(message, LBUF_SIZE-1, buf, &bp, executor, caller, enactor, eval|EV_FCHECK|EV_EVAL|EV_TOP,
|
2018-10-03 17:54:51 +00:00
|
|
|
|
nullptr, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify(executor, buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_say(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *message, 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);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Make sure speaker is somewhere if speaking in a place
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref loc = where_is(executor);
|
|
|
|
|
|
if ( !( Good_obj(loc)
|
2007-01-01 19:22:40 +00:00
|
|
|
|
&& sp_ok(executor)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int say_flags, depth;
|
|
|
|
|
|
|
|
|
|
|
|
// Convert prefix-coded messages into the normal type
|
|
|
|
|
|
//
|
2007-01-02 21:04:54 +00:00
|
|
|
|
say_flags = key & (SAY_NOEVAL | SAY_HERE | SAY_ROOM | SAY_HTML);
|
|
|
|
|
|
key &= ~(SAY_NOEVAL | SAY_HERE | SAY_ROOM | SAY_HTML);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (key == SAY_PREFIX)
|
|
|
|
|
|
{
|
2007-12-09 00:53:07 +00:00
|
|
|
|
switch (message[0])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
case '"':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
key = SAY_SAY;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
if (*message == ' ')
|
|
|
|
|
|
{
|
|
|
|
|
|
message++;
|
|
|
|
|
|
key = SAY_POSE_NOSPC;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
key = SAY_POSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
key = SAY_POSE_NOSPC;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2007-12-09 00:53:07 +00:00
|
|
|
|
case 0xE2:
|
|
|
|
|
|
if ( 0x80 == message[1]
|
|
|
|
|
|
&& 0x9C == message[2])
|
|
|
|
|
|
{
|
|
|
|
|
|
// U+201C - Unicode version of opening double quote.
|
|
|
|
|
|
//
|
|
|
|
|
|
message += 3;
|
|
|
|
|
|
key = SAY_SAY;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
key = SAY_EMIT;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
case '\\':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
|
|
|
|
|
|
// FALLTHROUGH
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
key = SAY_EMIT;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:39:35 -07:00
|
|
|
|
const UTF8 *command = T("");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (SAY_SAY == key)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
command = T("say");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (SAY_POSE == key || SAY_POSE_NOSPC == key)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
command = T("pose");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (SAY_EMIT == key)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
command = T("@emit");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Parse speechmod if present.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *messageOrig = message;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *messageNew = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!(say_flags & SAY_NOEVAL))
|
|
|
|
|
|
{
|
|
|
|
|
|
messageNew = modSpeech(executor, message, true, command);
|
|
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = messageNew;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Send the message on its way
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *saystring;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SAY_SAY:
|
|
|
|
|
|
saystring = modSpeech(executor, messageOrig, false, command);
|
|
|
|
|
|
if (saystring)
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_saypose(executor, tprintf(M_("%s %s “%s”"),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Moniker(executor), saystring, message));
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_except_rlevel(loc, executor, executor, tprintf(M_("%s %s “%s”"), Moniker(executor), saystring, message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_except(loc, executor, executor, tprintf(M_("%s %s “%s”"), Moniker(executor), saystring, message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
free_lbuf(saystring);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_saypose(executor, tprintf(M_("You say, “%s”"), message));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#ifdef REALITY_LVLS
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_except_rlevel(loc, executor, executor, tprintf(M_("%s says, “%s”"), Moniker(executor), message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_except(loc, executor, executor, tprintf(M_("%s says, “%s”"), Moniker(executor), message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SAY_POSE:
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2026-07-28 09:03:29 -06:00
|
|
|
|
notify_except_rlevel(loc, executor, -1, tprintf(M_("%s %s"), Moniker(executor), message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2026-07-28 09:03:29 -06:00
|
|
|
|
notify_all_from_inside_saypose(loc, executor, tprintf(M_("%s %s"), Moniker(executor), message));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SAY_POSE_NOSPC:
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
2026-07-28 09:03:29 -06:00
|
|
|
|
notify_except_rlevel(loc, executor, -1, tprintf(M_("%s%s"), Moniker(executor), message), MSG_SAYPOSE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#else
|
2026-07-28 09:03:29 -06:00
|
|
|
|
notify_all_from_inside_saypose(loc, executor, tprintf(M_("%s%s"), Moniker(executor), message));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SAY_EMIT:
|
|
|
|
|
|
if ( (say_flags & SAY_HERE)
|
|
|
|
|
|
|| (say_flags & SAY_HTML)
|
|
|
|
|
|
|| !say_flags)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (say_flags & SAY_HTML)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_all_from_inside_html(loc, executor, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
|
|
|
|
|
notify_except_rlevel(loc, executor, -1, message, SAY_EMIT);
|
|
|
|
|
|
#else
|
|
|
|
|
|
notify_all_from_inside(loc, executor, message);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (say_flags & SAY_ROOM)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( isRoom(loc)
|
|
|
|
|
|
&& (say_flags & SAY_HERE))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(messageNew);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (depth = 0; !isRoom(loc) && (depth < 20); depth++)
|
|
|
|
|
|
{
|
|
|
|
|
|
loc = Location(loc);
|
|
|
|
|
|
if ( !Good_obj(loc)
|
|
|
|
|
|
|| (loc == Location(loc)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(messageNew);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isRoom(loc))
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef REALITY_LVLS
|
|
|
|
|
|
notify_except_rlevel(loc, executor, -1, message, -1);
|
|
|
|
|
|
#else
|
|
|
|
|
|
notify_all_from_inside(loc, executor, message);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(messageNew);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-08 21:50:57 -06:00
|
|
|
|
struct wall_broadcast_context
|
|
|
|
|
|
{
|
|
|
|
|
|
int target;
|
|
|
|
|
|
dbref player;
|
|
|
|
|
|
UTF8 *message;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void wall_broadcast_callback(dbref connected_player, void *ctx)
|
2007-01-02 21:04:54 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
wall_broadcast_context *wbc = static_cast<wall_broadcast_context *>(ctx);
|
|
|
|
|
|
switch (wbc->target)
|
2007-01-02 21:04:54 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
case SHOUT_WIZARD:
|
|
|
|
|
|
if (Wizard(connected_player))
|
2007-01-02 21:04:54 +00:00
|
|
|
|
{
|
2026-03-08 21:50:57 -06:00
|
|
|
|
notify_with_cause(connected_player, wbc->player, wbc->message);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2022-03-15 19:47:08 -06:00
|
|
|
|
|
2026-03-08 21:50:57 -06:00
|
|
|
|
case SHOUT_ADMIN:
|
|
|
|
|
|
if (WizRoy(connected_player))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_with_cause(connected_player, wbc->player, wbc->message);
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
2026-03-08 21:50:57 -06:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
notify_with_cause(connected_player, wbc->player, wbc->message);
|
|
|
|
|
|
break;
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-08 21:50:57 -06:00
|
|
|
|
static void wall_broadcast(int target, dbref player, UTF8 *message)
|
|
|
|
|
|
{
|
|
|
|
|
|
wall_broadcast_context ctx = { target, player, message };
|
|
|
|
|
|
for_each_connected_player(wall_broadcast_callback, &ctx);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 22:33:04 +00:00
|
|
|
|
static const UTF8 *announce_msg = T("Announcement: ");
|
|
|
|
|
|
static const UTF8 *broadcast_msg = T("Broadcast: ");
|
|
|
|
|
|
static const UTF8 *admin_msg = T("Admin: ");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2006-09-06 06:17:46 +00:00
|
|
|
|
void do_shout(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UTF8 *message, 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);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *p = nullptr, *messageNew = nullptr, *buf2 = nullptr, *bp = nullptr;
|
2007-01-02 21:04:54 +00:00
|
|
|
|
bool bNoTag = (key & SHOUT_NOTAG) ? true : false;
|
|
|
|
|
|
bool bEmit = (key & SHOUT_EMIT) ? true : false;
|
|
|
|
|
|
bool bPose = (key & SHOUT_POSE) ? true : bEmit;
|
|
|
|
|
|
bool bSpace = !bEmit;
|
|
|
|
|
|
key &= ~(SHOUT_NOTAG | SHOUT_POSE | SHOUT_EMIT);
|
2007-03-14 00:55:08 +00:00
|
|
|
|
static const UTF8 *prefix, *loghead, *logtext1, *logsay, *saystring;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-01-02 21:04:54 +00:00
|
|
|
|
if (key & SHOUT_ADMIN)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-01-02 21:04:54 +00:00
|
|
|
|
key = SHOUT_ADMIN; // @wall/wiz/admin is treated as @wall/admin
|
|
|
|
|
|
prefix = admin_msg;
|
2007-03-14 22:33:04 +00:00
|
|
|
|
loghead = T("ASHOUT");
|
|
|
|
|
|
logtext1 = T(" ADMIN");
|
|
|
|
|
|
logsay = T(" yells: ");
|
2026-07-26 21:52:48 -06:00
|
|
|
|
saystring = T("says, “");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-01-02 21:04:54 +00:00
|
|
|
|
else if (key & SHOUT_WIZARD)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-01-02 21:04:54 +00:00
|
|
|
|
prefix = broadcast_msg;
|
2007-03-14 22:33:04 +00:00
|
|
|
|
loghead = T("BCAST");
|
|
|
|
|
|
logtext1 = T(" WIZ");
|
|
|
|
|
|
logsay = T(" broadcasts: ");
|
2026-07-26 21:52:48 -06:00
|
|
|
|
saystring = T("says, “");
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
prefix = announce_msg;
|
2007-03-14 22:33:04 +00:00
|
|
|
|
loghead = T("SHOUT");
|
|
|
|
|
|
logtext1 = T(" WALL");
|
|
|
|
|
|
logsay = T(" shouts: ");
|
2026-07-26 21:52:48 -06:00
|
|
|
|
saystring = T("shouts, “");
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-01-02 21:04:54 +00:00
|
|
|
|
if (bNoTag)
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
prefix = T("");
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-01-02 21:04:54 +00:00
|
|
|
|
if (!bPose)
|
|
|
|
|
|
{
|
2006-09-01 22:59:23 +00:00
|
|
|
|
switch (*message)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ';':
|
2007-01-02 21:04:54 +00:00
|
|
|
|
bSpace = false;
|
|
|
|
|
|
// FALL THROUGH
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
case ':':
|
2007-01-02 21:04:54 +00:00
|
|
|
|
bPose = true;
|
|
|
|
|
|
// FALL THROUGH
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
case '"':
|
|
|
|
|
|
message++;
|
2007-01-02 21:04:54 +00:00
|
|
|
|
break;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2007-01-02 21:04:54 +00:00
|
|
|
|
// Parse speechmod if present.
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
messageNew = modSpeech(executor, message, true, T("@wall"));
|
2007-01-02 21:04:54 +00:00
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = messageNew;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!bPose)
|
|
|
|
|
|
{
|
|
|
|
|
|
buf2 = alloc_lbuf("do_shout");
|
|
|
|
|
|
bp = buf2;
|
|
|
|
|
|
safe_str(saystring, buf2, &bp);
|
|
|
|
|
|
safe_str(message, buf2, &bp);
|
2026-07-26 21:52:48 -06:00
|
|
|
|
safe_str(T("”"), buf2, &bp);
|
2007-01-02 21:04:54 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
}
|
2026-07-28 09:03:29 -06:00
|
|
|
|
p = tprintf(M_("%s%s%s%s"), prefix, bEmit ? T("") : Moniker(executor),
|
2026-03-03 18:39:35 -07:00
|
|
|
|
bSpace ? T(" ") : T(""), bPose ? reinterpret_cast<UTF8 *>(message) : reinterpret_cast<UTF8 *>(buf2));
|
2007-01-02 21:04:54 +00:00
|
|
|
|
wall_broadcast(key, executor, p);
|
|
|
|
|
|
if (!bPose)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(buf2);
|
|
|
|
|
|
}
|
|
|
|
|
|
STARTLOG(LOG_SHOUTS, "WIZ", loghead);
|
|
|
|
|
|
log_name(executor);
|
|
|
|
|
|
if (bEmit)
|
|
|
|
|
|
{
|
|
|
|
|
|
log_text(logtext1);
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T("emits: "));
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (bPose)
|
|
|
|
|
|
{
|
|
|
|
|
|
log_text(logtext1);
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T("poses: "));
|
2007-01-02 21:04:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
log_text(logsay);
|
|
|
|
|
|
}
|
|
|
|
|
|
log_text(message);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (messageNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(messageNew);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* do_page: Handle the page command.
|
|
|
|
|
|
* Page-pose code from shadow@prelude.cc.purdue.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
static void page_return(dbref player, dbref target, const UTF8 *tag,
|
|
|
|
|
|
int anum, const UTF8 *dflt)
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
UTF8 *bp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf str = LBuf_Adopt(atr_pget(target, anum, &aowner, &aflags));
|
|
|
|
|
|
if (*str.get())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf str2 = LBuf_Src("page_return");
|
|
|
|
|
|
bp = str2;
|
2026-03-07 19:57:24 -07:00
|
|
|
|
mux_exec(str, LBUF_SIZE-1, str2, &bp, target, player, player,
|
2006-09-06 04:52:56 +00:00
|
|
|
|
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP|EV_NO_LOCATION),
|
2018-10-03 17:54:51 +00:00
|
|
|
|
nullptr, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*bp = '\0';
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
if (*str2.get())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeAbsolute ltaNow;
|
|
|
|
|
|
ltaNow.GetLocal();
|
|
|
|
|
|
FIELDEDTIME ft;
|
|
|
|
|
|
ltaNow.ReturnFields(&ft);
|
|
|
|
|
|
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
UTF8 *p = tprintf(M_("%s message from %s: %s"), tag,
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
Moniker(target), str2.get());
|
2012-04-11 10:44:10 -07:00
|
|
|
|
notify_with_cause_ooc(player, target, p, MSG_SRC_PAGE);
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
p = tprintf(M_("[%d:%02d] %s message sent to %s."), ft.iHour,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ft.iMinute, tag, Moniker(player));
|
2012-04-11 10:44:10 -07:00
|
|
|
|
notify_with_cause_ooc(target, player, p, MSG_SRC_PAGE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (dflt && *dflt)
|
|
|
|
|
|
{
|
2012-04-11 10:44:10 -07:00
|
|
|
|
notify_with_cause_ooc(player, target, dflt, MSG_SRC_PAGE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool page_check(dbref player, dbref target)
|
|
|
|
|
|
{
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
// Validate first and charge last. payfor() used to run before any of
|
|
|
|
|
|
// these checks and nothing refunded it when they failed, so paging an
|
|
|
|
|
|
// offline player -- or one whose PAGE-lock rejected the sender -- still
|
|
|
|
|
|
// cost pagecost and delivered nothing. do_page() calls this once per
|
|
|
|
|
|
// recipient, so a multi-target page multiplied the loss (#1187).
|
|
|
|
|
|
//
|
|
|
|
|
|
// payfor() deducts only on success, so moving it last needs no refund
|
|
|
|
|
|
// path.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!Connected(target))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
page_return(player, target, T("Away"), A_AWAY,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, %s is not connected."), Moniker(target)));
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
return false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
|
|
|
|
|
|
if (!could_doit(player, target, A_LPAGE))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if ( Can_Hide(target)
|
|
|
|
|
|
&& Hidden(target)
|
|
|
|
|
|
&& !See_Hidden(player))
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
page_return(player, target, T("Away"), A_AWAY,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, %s is not connected."), Moniker(target)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
page_return(player, target, T("Reject"), A_REJECT,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, %s is not accepting pages."), Moniker(target)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
return false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
|
|
|
|
|
|
// A wizard may page someone who cannot page back; everyone else may not.
|
|
|
|
|
|
// Hold the warning until the page is actually paid for, so a sender who
|
|
|
|
|
|
// cannot afford it does not get told about a page that never happens.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bCannotReturn = false;
|
|
|
|
|
|
if (!could_doit(target, player, A_LPAGE))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
if (!Wizard(player))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(player, tprintf(M_("Sorry, %s can’t return your page."),
|
2007-01-23 09:47:13 +00:00
|
|
|
|
Moniker(target)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
bCannotReturn = true;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
|
|
|
|
|
|
if (!payfor(player, Guest(player) ? 0 : mudconf.pagecost))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(player, tprintf(M_("You don’t have enough %s."), mudconf.many_coins));
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bCannotReturn)
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(player, tprintf(M_("Warning: %s can’t return your page."),
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
Moniker(target)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
fix: resolve Pass 8 user-facing defects #1182, #1184, #1186, #1187
Four unclaimed Pass 8 findings in four separate files, chosen to avoid the
files the open PR queue is already touching. Each premise was re-verified
against current source before fixing.
#1182 p6h_vaht_crypt OOB read (player.cpp). The guard only required
szSetting to be at least the 13-byte "$P6H$$1:sha1:" prefix, but the
timestamp is copied from a constant offset 54 bytes in (prefix + 40 hex
digits + separator). Any A_PASS carrying that prefix with a total length
of 13..53 passed the check and then ran safe_str off the end of the
attribute value. Reachable because mux_crypt classifies anything starting
"$P6H$" (and not "XX") as CRYPT_P6H_VAHT, so a truncated or corrupt A_PASS
-- raw attribute write or a damaged import, not @password -- reaches it on
the login path. Require the whole fixed layout including the separator,
and fail closed to szFail.
#1184 CONNECTED leak via decode_flags (flags.cpp). has_flag() and
flag_description() both hide the 'c' letter on Hidden(target) &&
!See_Hidden(player). decode_flags() required (WIZARD | DARK) together, so
every dark non-wizard -- royalty, staff, any mortal able to set itself DARK
-- still emitted 'c' to examiners. Hidden(x) is exactly (Flags(x) & DARK),
and decode_flags takes a FLAGSET rather than a dbref, so the aligned test
is DARK on the caller's flagset (unparse_object passes the target's).
#1186 moniker injection in look_contents (look.cpp). look_exits()
html_escape()s exit names inside xch_cmd="...", and the anchor text in
look_contents() was already escaped, but CONTENTS_LOCAL and CONTENTS_NESTED
inserted Moniker() raw into the attribute. A moniker containing a double
quote closed the attribute early and let the rest become further
Pueblo/HTML markup for HTML-capable clients. CONTENTS_REMOTE was already
safe (it emits #dbref).
#1187 page_check charged before validating (speech.cpp). payfor() ran
first; the not-connected and both A_LPAGE lock failures then returned false
with no refund, so a page that was never delivered still cost page_cost --
once per recipient, since do_page() calls page_check() per target.
Reordered to validate first and charge last; payfor() deducts only on
success, so no refund path is needed. The wizard "can't return your page"
warning is now held until after payment, so a sender who cannot afford the
page is not told about one that never happens.
Behaviour change worth noting: when a sender both lacks funds and the
target is offline, the message is now "Sorry, X is not connected." rather
than "You don't have enough coins." -- the actual reason rather than the
one that happened to be checked first.
Adds tests/scenario/page_cost.py (wired into run.sh) for #1187, the only
one of the four reachable without an HTML client or manufactured
connection state. It needs a mortal sender, since payfor() exempts
wizards outright and a Wizard-only test would pass against any
implementation.
Test validated against the unfixed build: cases 2 and 4 fail there
(offline page charged 5; two offline recipients charged 10, showing the
per-recipient amplification) while case 3 passes in both -- so it pins the
charge, not merely the absence of one.
The other three are not smoke-reachable: #1182 needs a crafted A_PASS,
#1184 needs live CONNECTED state on a dark player, #1186 needs an HTML
client. Their normal paths are covered -- every scenario driver logs in
through mux_crypt, and all four suites pass.
Verified: build clean, smoke 1404/1404 0 crashes, scenario 4/4 drivers
(wild_capture, site_threshold, jit_perms, telnet_negotiation) plus the new
page_cost 4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:35:29 -06:00
|
|
|
|
return true;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The combinations are:
|
|
|
|
|
|
//
|
|
|
|
|
|
// nargs arg1[0] arg2[0]
|
|
|
|
|
|
// '' 1 '\0' '\0' Report LastPaged to player.
|
|
|
|
|
|
// 'a' 1 'a' '\0' Page LastPaged with A
|
|
|
|
|
|
// 'a=' 2 'a' '\0' Page A. LastPaged <- A
|
|
|
|
|
|
// '=b' 2 '\0' 'b' Page LastPaged with B
|
|
|
|
|
|
// 'a=b' 2 'a' 'b' Page A with B. LastPaged <- A
|
|
|
|
|
|
// 'a=b1=[b2=]*...' All treated the same as 'a=b'.
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_page
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
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 *arg1,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *arg2,
|
|
|
|
|
|
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:03:45 -07:00
|
|
|
|
bool bBlind = (key & PAGE_BLIND) != 0;
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int nPlayers = 0;
|
|
|
|
|
|
dbref aPlayers[(LBUF_SIZE+1)/2];
|
|
|
|
|
|
|
|
|
|
|
|
// Either we have been given a recipient list, or we are relying on an
|
|
|
|
|
|
// existing A_LASTPAGE.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bModified = false;
|
|
|
|
|
|
if ( nargs == 2
|
|
|
|
|
|
&& arg1[0] != '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
bModified = true;
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *p = arg1;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
while (*p != '\0')
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
UTF8 *q = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(p), '"'));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Decode space-delimited or comma-delimited recipients.
|
|
|
|
|
|
//
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(p, T(", "));
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *r;
|
2022-03-11 12:36:37 -07:00
|
|
|
|
for (r = st.parse(); r; r = st.parse())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref target = lookup_player(executor, r, true);
|
|
|
|
|
|
if (target != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("I don’t recognize “%s”."), r));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
p = q + 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Handle quoted named.
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
q = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(p), '"'));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref target = lookup_player(executor, p, true);
|
|
|
|
|
|
if (target != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("I don’t recognize “%s”."), p));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
p = q + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Need to decode the A_LASTPAGE.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf pLastPage = LBuf_Adopt(atr_get("do_page.645", executor, A_LASTPAGE, &aowner, &aflags));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(pLastPage, T(" "));
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *p;
|
2022-03-11 12:36:37 -07:00
|
|
|
|
for (p = st.parse(); p; p = st.parse())
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
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
|
|
|
|
dbref target = mux_atoi64(p);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( Good_obj(target)
|
|
|
|
|
|
&& isPlayer(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("I don’t recognize #%d."), target));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bModified = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int nValid = nPlayers;
|
|
|
|
|
|
|
|
|
|
|
|
// Remove duplicate dbrefs.
|
|
|
|
|
|
//
|
|
|
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < nPlayers-1; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[i] != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
int j;
|
|
|
|
|
|
for (j = i+1; j < nPlayers; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[j] == aPlayers[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[j] = NOTHING;
|
|
|
|
|
|
bModified = true;
|
|
|
|
|
|
nValid--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we are doing more than reporting, we have some other dbref
|
|
|
|
|
|
// validation to do.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nargs == 2
|
|
|
|
|
|
|| arg1[0] != '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
for (i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Good_obj(aPlayers[i])
|
|
|
|
|
|
&& !page_check(executor, aPlayers[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[i] = NOTHING;
|
|
|
|
|
|
bModified = true;
|
|
|
|
|
|
nValid--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bModified)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Our aPlayers could be different than the one encoded on A_LASTPAGE.
|
|
|
|
|
|
// Update the database.
|
|
|
|
|
|
//
|
|
|
|
|
|
ITL itl;
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf pBuff = LBuf_Src("do_page.lastpage");
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *pBufc = pBuff;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ItemToList_Init(&itl, pBuff, &pBufc);
|
|
|
|
|
|
for (i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Good_obj(aPlayers[i])
|
|
|
|
|
|
&& !ItemToList_AddInteger(&itl, aPlayers[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ItemToList_Final(&itl);
|
|
|
|
|
|
atr_add_raw(executor, A_LASTPAGE, pBuff);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that the recipient list isn't empty.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (nValid == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( nargs == 1
|
|
|
|
|
|
&& arg1[0] == '\0')
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(executor, M_("You have not paged anyone."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(executor, M_("No one to page."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build a friendly representation of the recipient list.
|
|
|
|
|
|
//
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf aFriendly = LBuf_Src("do_page.friendly");
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *pFriendly = aFriendly;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (nValid > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_chr('(', aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
bool bFirst = true;
|
|
|
|
|
|
for (i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[i] != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bFirst)
|
|
|
|
|
|
{
|
|
|
|
|
|
bFirst = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_copy_buf(T(", "), 2, aFriendly, &pFriendly);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
safe_str(Moniker(aPlayers[i]), aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (nValid > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_chr(')', aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
*pFriendly = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
// We may be able to proceed directly to the reporting case.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nargs == 1
|
|
|
|
|
|
&& arg1[0] == '\0')
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("You last paged %s."), aFriendly.get()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build messages.
|
|
|
|
|
|
//
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf omessage = LBuf_Src("do_page.omessage");
|
|
|
|
|
|
LBuf imessage = LBuf_Src("do_page.imessage");
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *omp = omessage;
|
|
|
|
|
|
UTF8 *imp = imessage;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *pMessage;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (nargs == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 'page A' form.
|
|
|
|
|
|
//
|
|
|
|
|
|
pMessage = arg1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 'page A=', 'page =B', and 'page A=B' forms.
|
|
|
|
|
|
//
|
|
|
|
|
|
pMessage = arg2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pageMode;
|
|
|
|
|
|
switch (*pMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
case '\0':
|
|
|
|
|
|
pageMode = 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
|
|
pMessage++;
|
2008-03-02 22:19:57 -08:00
|
|
|
|
if (' ' != *pMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
pageMode = 2;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FALL THROUGH
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
|
|
pageMode = 3;
|
|
|
|
|
|
pMessage++;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
|
|
pMessage++;
|
|
|
|
|
|
|
|
|
|
|
|
// FALL THROUGH
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
pageMode = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 18:39:35 -07:00
|
|
|
|
UTF8 *newMessage = modSpeech(executor, pMessage, true, T("page"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
pMessage = newMessage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (pageMode)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
// 'page A=' form.
|
|
|
|
|
|
//
|
2026-03-04 12:03:45 -07:00
|
|
|
|
if (bBlind || nValid == 1)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, %s pages you."),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Moniker(executor));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, %s pages %s."),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
Moniker(executor), aFriendly.get());
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(imessage, &imp, M_("You page %s."), aFriendly.get());
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 2:
|
2026-07-27 16:15:26 +00:00
|
|
|
|
// Whole sentences: the recipient list is conditional, so two
|
|
|
|
|
|
// msgids rather than T/M_ fragment assembly (see #1575 / #1588).
|
|
|
|
|
|
//
|
2026-03-04 12:03:45 -07:00
|
|
|
|
if (!bBlind && nValid > 1)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 16:15:26 +00:00
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, to %s: %s %s"),
|
|
|
|
|
|
aFriendly.get(), Moniker(executor), pMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, %s %s"),
|
|
|
|
|
|
Moniker(executor), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(imessage, &imp, M_("Long distance to %s: %s %s"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), Moniker(executor), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 3:
|
2026-03-04 12:03:45 -07:00
|
|
|
|
if (!bBlind && nValid > 1)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 16:15:26 +00:00
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, to %s: %s%s"),
|
|
|
|
|
|
aFriendly.get(), Moniker(executor), pMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("From afar, %s%s"),
|
|
|
|
|
|
Moniker(executor), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(imessage, &imp, M_("Long distance to %s: %s%s"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), Moniker(executor), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-03-04 12:03:45 -07:00
|
|
|
|
if (!bBlind && nValid > 1)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 16:15:26 +00:00
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("To %s, %s pages: %s"),
|
|
|
|
|
|
aFriendly.get(), Moniker(executor), pMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_tprintf_str(omessage, &omp, M_("%s pages: %s"),
|
|
|
|
|
|
Moniker(executor), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
safe_tprintf_str(imessage, &imp, M_("You paged %s with ‘%s’"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), pMessage);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to recipients.
|
|
|
|
|
|
//
|
|
|
|
|
|
for (i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref target = aPlayers[i];
|
|
|
|
|
|
if (target != NOTHING)
|
|
|
|
|
|
{
|
2012-04-11 10:44:10 -07:00
|
|
|
|
notify_with_cause_ooc(target, executor, omessage, MSG_SRC_PAGE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int target_idle = fetch_idle(target);
|
2026-07-28 19:34:54 -06:00
|
|
|
|
int64_t target_idle_timeout_val = idle_timeout_val(target);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (target_idle >= target_idle_timeout_val)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
page_return(executor, target, T("Idle"), A_IDLE, nullptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Send message to sender.
|
|
|
|
|
|
//
|
|
|
|
|
|
notify(executor, imessage);
|
|
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
|
|
* do_pemit: Messages to specific players, or to all but specific players.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
static void whisper_pose(dbref player, dbref target, UTF8 *message, bool bSpace)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
UTF8 *newMessage = modSpeech(player, message, true, T("whisper"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = newMessage;
|
|
|
|
|
|
}
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf buff = LBuf_Src("do_pemit.whisper.pose");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
mux_strncpy(buff, Moniker(player), LBUF_SIZE-1);
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify_with_cause(target, player, tprintf(M_("You sense %s%s%s"), buff.get(),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bSpace ? " " : "", message));
|
|
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-31 22:13:06 -07:00
|
|
|
|
static dbref FindPemitTarget(dbref player, int key, UTF8 *recipient)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref target = NOTHING;
|
|
|
|
|
|
|
|
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PEMIT_FSAY:
|
|
|
|
|
|
case PEMIT_FPOSE:
|
|
|
|
|
|
case PEMIT_FPOSE_NS:
|
|
|
|
|
|
case PEMIT_FEMIT:
|
|
|
|
|
|
target = match_controlled(player, recipient);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
init_match(player, recipient, TYPE_PLAYER);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
target = match_result();
|
|
|
|
|
|
}
|
|
|
|
|
|
return target;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
void do_pemit_single
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
int key,
|
|
|
|
|
|
bool bDoContents,
|
|
|
|
|
|
int pemit_flags,
|
2008-09-20 07:29:53 -07:00
|
|
|
|
dbref target,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int chPoseType,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *message
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
2008-03-31 22:13:06 -07:00
|
|
|
|
dbref loc;
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
UTF8 *bp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int depth;
|
|
|
|
|
|
bool ok_to_do = false;
|
|
|
|
|
|
|
|
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PEMIT_FSAY:
|
|
|
|
|
|
case PEMIT_FPOSE:
|
|
|
|
|
|
case PEMIT_FPOSE_NS:
|
|
|
|
|
|
case PEMIT_FEMIT:
|
|
|
|
|
|
if (target == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
ok_to_do = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *newMessage = nullptr;
|
|
|
|
|
|
UTF8 *saystring = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *p;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
switch (target)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NOTHING:
|
|
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PEMIT_WHISPER:
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Whisper to whom?"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_PEMIT:
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Emit to whom?"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_OEMIT:
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Emit except to whom?"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("Sorry."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case AMBIGUOUS:
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("I don’t know who you mean!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
|
|
// Enforce locality constraints.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !ok_to_do
|
|
|
|
|
|
&& ( nearby(player, target)
|
|
|
|
|
|
|| Long_Fingers(player)
|
|
|
|
|
|
|| Controls(player, target)))
|
|
|
|
|
|
{
|
|
|
|
|
|
ok_to_do = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !ok_to_do
|
|
|
|
|
|
&& key == PEMIT_PEMIT
|
|
|
|
|
|
&& isPlayer(target)
|
|
|
|
|
|
&& mudconf.pemit_players)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!page_check(player, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
ok_to_do = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !ok_to_do
|
|
|
|
|
|
&& ( !mudconf.pemit_any
|
|
|
|
|
|
|| key != PEMIT_PEMIT))
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("You are too far away to do that."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( bDoContents
|
|
|
|
|
|
&& !Controls(player, target)
|
|
|
|
|
|
&& !mudconf.pemit_any)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(player, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
loc = where_is(target);
|
|
|
|
|
|
|
|
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PEMIT_PEMIT:
|
|
|
|
|
|
if (bDoContents)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Has_contents(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_all_from_inside(target, player, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pemit_flags & PEMIT_HTML)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_with_cause_html(target, player, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_with_cause(target, player, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_OEMIT:
|
|
|
|
|
|
notify_except(Location(target), player, target, message, 0);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_WHISPER:
|
|
|
|
|
|
if ( isPlayer(target)
|
|
|
|
|
|
&& !Connected(target))
|
|
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
page_return(player, target, T("Away"), A_AWAY,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, %s is not connected."), Moniker(target)));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (chPoseType)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ':':
|
|
|
|
|
|
message++;
|
2026-03-04 11:37:35 -07:00
|
|
|
|
if (' ' == *message)
|
|
|
|
|
|
{
|
|
|
|
|
|
message++;
|
|
|
|
|
|
whisper_pose(player, target, message, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
whisper_pose(player, target, message, true);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
whisper_pose(player, target, message, false);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
|
|
message++;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-03-03 18:39:35 -07:00
|
|
|
|
newMessage = modSpeech(player, message, true, T("whisper"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = newMessage;
|
|
|
|
|
|
}
|
|
|
|
|
|
notify_with_cause(target, player,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("%s whispers “%s”"), Moniker(player), message));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !mudconf.quiet_whisper
|
|
|
|
|
|
&& !Wizard(player))
|
|
|
|
|
|
{
|
|
|
|
|
|
loc = where_is(player);
|
|
|
|
|
|
if (loc != NOTHING)
|
|
|
|
|
|
{
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf buf2 = LBuf_Src("do_pemit.whisper.buzz");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bp = buf2;
|
|
|
|
|
|
safe_str(Moniker(player), buf2, &bp);
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_str(T(" whispers something to "), buf2, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_str(Moniker(target), buf2, &bp);
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify_except2(loc, player, player, target, buf2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_FSAY:
|
2026-03-03 18:39:35 -07:00
|
|
|
|
newMessage = modSpeech(target, message, true, T("@fsay"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = newMessage;
|
|
|
|
|
|
}
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(target, tprintf(M_("You say, “%s”"), message));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (loc != NOTHING)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
saystring = modSpeech(target, message, false, T("@fsay"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (saystring)
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
p = tprintf(M_("%s %s “%s”"), Moniker(target),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
saystring, message);
|
|
|
|
|
|
notify_except(loc, player, target, p, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
p = tprintf(M_("%s says, “%s”"), Moniker(target),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
message);
|
|
|
|
|
|
notify_except(loc, player, target, p, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (saystring)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(saystring);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_FPOSE:
|
2026-03-03 18:39:35 -07:00
|
|
|
|
newMessage = modSpeech(target, message, true, T("@fpose"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = newMessage;
|
|
|
|
|
|
}
|
2026-07-28 09:03:29 -06:00
|
|
|
|
p = tprintf(M_("%s %s"), Moniker(target), message);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
notify_all_from_inside(loc, player, p);
|
|
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_FPOSE_NS:
|
2026-03-03 18:39:35 -07:00
|
|
|
|
newMessage = modSpeech(target, message, true, T("@fpose"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = newMessage;
|
|
|
|
|
|
}
|
2026-07-28 09:03:29 -06:00
|
|
|
|
p = tprintf(M_("%s%s"), Moniker(target), message);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
notify_all_from_inside(loc, player, p);
|
|
|
|
|
|
if (newMessage)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(newMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case PEMIT_FEMIT:
|
|
|
|
|
|
if ( (pemit_flags & PEMIT_HERE)
|
|
|
|
|
|
|| !pemit_flags)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_all_from_inside(loc, player, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pemit_flags & PEMIT_ROOM)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( isRoom(loc)
|
|
|
|
|
|
&& (pemit_flags & PEMIT_HERE))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
depth = 0;
|
|
|
|
|
|
while ( !isRoom(loc)
|
|
|
|
|
|
&& depth++ < 20)
|
|
|
|
|
|
{
|
|
|
|
|
|
loc = Location(loc);
|
|
|
|
|
|
if ( loc == NOTHING
|
|
|
|
|
|
|| loc == Location(loc))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isRoom(loc))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_all_from_inside(loc, player, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-09-19 18:33:51 -07:00
|
|
|
|
void do_pemit_single
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
int key,
|
|
|
|
|
|
bool bDoContents,
|
|
|
|
|
|
int pemit_flags,
|
|
|
|
|
|
UTF8 *recipient,
|
|
|
|
|
|
int chPoseType,
|
|
|
|
|
|
UTF8 *message
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref target = FindPemitTarget(player, key, recipient);
|
2008-09-20 07:29:53 -07:00
|
|
|
|
do_pemit_single(player, key, bDoContents, pemit_flags, target, chPoseType, message);
|
2008-09-19 18:33:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
void do_pemit_list
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref player,
|
|
|
|
|
|
int key,
|
|
|
|
|
|
bool bDoContents,
|
|
|
|
|
|
int pemit_flags,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *list,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int chPoseType,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *message
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if ( '\0' == message[0]
|
|
|
|
|
|
|| '\0' == list[0])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
UTF8 *error_message = nullptr;
|
|
|
|
|
|
UTF8 *error_ptr = nullptr;
|
2008-09-19 18:33:51 -07:00
|
|
|
|
dbref aPlayers[(LBUF_SIZE+1)/2];
|
2008-09-20 07:29:53 -07:00
|
|
|
|
int nPlayers = 0;
|
2008-03-31 22:13:06 -07:00
|
|
|
|
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(list, T(", "));
|
|
|
|
|
|
for (UTF8 *p = st.parse(); p; p = st.parse())
|
2008-03-31 22:13:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
dbref target = FindPemitTarget(player, key, p);
|
|
|
|
|
|
|
2008-09-20 07:29:53 -07:00
|
|
|
|
if (Good_obj(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
2008-09-19 18:33:51 -07:00
|
|
|
|
}
|
2008-09-20 07:29:53 -07:00
|
|
|
|
else
|
2008-03-31 22:13:06 -07:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr == error_message)
|
2008-03-31 22:13:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
error_message = alloc_lbuf("do_pemit_list.error");
|
|
|
|
|
|
error_ptr = error_message;
|
|
|
|
|
|
safe_str(T("Emit error(s): "), error_message, &error_ptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_str(T(", "), error_message, &error_ptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
safe_str(p, error_message, &error_ptr);
|
|
|
|
|
|
|
2008-04-24 07:23:21 -07:00
|
|
|
|
switch (target)
|
2008-03-31 22:13:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
case NOTHING:
|
|
|
|
|
|
safe_str(T(" (unknown)"), error_message, &error_ptr);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case AMBIGUOUS:
|
|
|
|
|
|
safe_str(T(" (ambiguous)"), error_message, &error_ptr);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-09-19 18:33:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Remove duplicate dbrefs.
|
|
|
|
|
|
//
|
|
|
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < nPlayers-1; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[i] != NOTHING)
|
2008-03-31 22:13:06 -07:00
|
|
|
|
{
|
2008-09-19 18:33:51 -07:00
|
|
|
|
int j;
|
|
|
|
|
|
for (j = i+1; j < nPlayers; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[j] == aPlayers[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[j] = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-03-31 22:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-04 13:47:30 -07:00
|
|
|
|
// For PEMIT_OEMIT, emit to the room of the first valid target,
|
|
|
|
|
|
// excluding all listed targets.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (PEMIT_OEMIT == key)
|
2008-09-20 07:29:53 -07:00
|
|
|
|
{
|
2026-03-04 13:47:30 -07:00
|
|
|
|
// Find first valid target.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref first_target = NOTHING;
|
|
|
|
|
|
for (int i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (NOTHING != aPlayers[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
first_target = aPlayers[i];
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (NOTHING != first_target)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Enforce locality constraints.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nearby(player, first_target)
|
|
|
|
|
|
|| Long_Fingers(player)
|
|
|
|
|
|
|| Controls(player, first_target))
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref loc = Location(first_target);
|
|
|
|
|
|
if (Good_obj(loc))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_except_N(loc, player, aPlayers, nPlayers, message, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(player, M_("You are too far away to do that."));
|
2026-03-04 13:47:30 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < nPlayers; i++)
|
2008-09-20 07:29:53 -07:00
|
|
|
|
{
|
2026-03-04 13:47:30 -07:00
|
|
|
|
dbref target = aPlayers[i];
|
|
|
|
|
|
if (NOTHING != target)
|
|
|
|
|
|
{
|
|
|
|
|
|
do_pemit_single(player, key, bDoContents, pemit_flags, target, chPoseType, message);
|
|
|
|
|
|
}
|
2008-09-20 07:29:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-09-19 18:33:51 -07:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != error_message)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2008-03-31 22:13:06 -07:00
|
|
|
|
*error_ptr = '\0';
|
|
|
|
|
|
notify(player, error_message);
|
|
|
|
|
|
free_lbuf(error_message);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
error_message = nullptr;
|
|
|
|
|
|
error_ptr = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-04-24 07:23:21 -07:00
|
|
|
|
// Check if the target can be contacted by executor. If not, issue an
|
|
|
|
|
|
// error message to the executor and return false. If checks pass, return
|
|
|
|
|
|
// true.
|
|
|
|
|
|
//
|
|
|
|
|
|
static bool noisy_check_whisper_target(dbref executor, dbref target, int key)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ok_to_do = false;
|
|
|
|
|
|
|
|
|
|
|
|
if ( nearby(executor, target)
|
|
|
|
|
|
|| Long_Fingers(executor)
|
|
|
|
|
|
|| Controls(executor, target))
|
|
|
|
|
|
{
|
|
|
|
|
|
ok_to_do = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( !ok_to_do
|
|
|
|
|
|
&& ( !mudconf.pemit_any
|
|
|
|
|
|
|| PEMIT_PEMIT != key))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, you are too far away to contact %s."),
|
2008-04-24 07:23:21 -07:00
|
|
|
|
Moniker(target)));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( isPlayer(target)
|
|
|
|
|
|
&& !Connected(target))
|
|
|
|
|
|
{
|
|
|
|
|
|
page_return(executor, target, T("Away"), A_AWAY,
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
tprintf(M_("Sorry, %s is not connected."),
|
2008-04-24 07:23:21 -07:00
|
|
|
|
Moniker(target)));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
|
|
|
|
|
|
void do_pemit_whisper
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
|
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
|
|
|
|
|
UTF8 *recipient,
|
|
|
|
|
|
UTF8 *message
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
// If the argument count is not 2, pull recipients from A_LASTWHISPER.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (nargs < 2)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2008-05-31 15:45:57 +00:00
|
|
|
|
if ('\0' == recipient[0])
|
|
|
|
|
|
{
|
2026-07-27 02:37:05 +00:00
|
|
|
|
notify(executor, M_("No one to whisper to."));
|
2008-05-31 15:45:57 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
message = recipient;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
recipient = nullptr;
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
bool bModified = true;
|
2007-08-18 01:53:56 +00:00
|
|
|
|
int nPlayers = 0;
|
|
|
|
|
|
dbref aPlayers[(LBUF_SIZE+1)/2];
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
// Read the A_LASTWHISPER attribute and use that recipient list.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( PEMIT_WHISPER == key
|
2018-10-03 17:54:51 +00:00
|
|
|
|
&& ( nullptr == recipient
|
2007-08-21 20:35:51 -07:00
|
|
|
|
|| '\0' == recipient[0]))
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf stored_recipient = LBuf_Adopt(
|
2008-01-29 21:00:01 -08:00
|
|
|
|
atr_get("do_whisper.1316", executor, A_LASTWHISPER,
|
2026-04-05 18:34:51 -06:00
|
|
|
|
&aowner, &aflags));
|
2007-08-18 01:53:56 +00:00
|
|
|
|
|
|
|
|
|
|
bModified = false;
|
|
|
|
|
|
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(stored_recipient, T(", "));
|
2007-08-18 01:53:56 +00:00
|
|
|
|
UTF8 *r;
|
2022-03-11 12:36:37 -07:00
|
|
|
|
for (r = st.parse(); r; r = st.parse())
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
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
|
|
|
|
dbref target = mux_atoi64(r);
|
2008-04-24 07:23:21 -07:00
|
|
|
|
if (Good_obj(target))
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2008-04-24 07:23:21 -07:00
|
|
|
|
if (!noisy_check_whisper_target(executor, target, key))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (bModified)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
UTF8 *p = recipient;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
while ('\0' != *p)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
UTF8 *q = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(p), '"'));
|
2007-08-18 01:53:56 +00:00
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Decode space-delimited or comma-delimited recipients.
|
|
|
|
|
|
//
|
2022-03-11 12:36:37 -07:00
|
|
|
|
string_token st(p, T(", "));
|
2007-08-18 01:53:56 +00:00
|
|
|
|
UTF8 *r;
|
2022-03-11 12:36:37 -07:00
|
|
|
|
for (r = st.parse(); r; r = st.parse())
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref target = lookup_player(executor, r, true);
|
2008-04-24 07:23:21 -07:00
|
|
|
|
|
|
|
|
|
|
if (NOTHING == target)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
init_match(executor, r, NOTYPE);
|
|
|
|
|
|
match_neighbor();
|
|
|
|
|
|
target = match_result();
|
2008-04-24 07:23:21 -07:00
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
|
2008-04-24 07:23:21 -07:00
|
|
|
|
if (NOTHING != target)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!noisy_check_whisper_target(executor, target, key))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (q)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
p = q + 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Handle quoted named.
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
q = reinterpret_cast<UTF8 *>(strchr(reinterpret_cast<char *>(p), '"'));
|
2007-08-18 01:53:56 +00:00
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref target = lookup_player(executor, p, true);
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (NOTHING != target)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):
- @clone/cost on an exit bypassed the "must control current location"
check (it lived only on the non-/cost path), letting a builder splice a
cloned exit into a room they do not control. (#855)
- The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
which does not exist (produces "Huh?"); corrected to @mark_all/clear.
(#856)
- @ps <object> reported nothing for a controlled object owned by another
player; do_ps was missing the non-player-target else clause that the
sibling @halt has (clear the owner filter). (#857)
- whisper "<quoted name>" skipped the locality/connected gate the unquoted
form applies, giving a success confirmation plus a delivery error and
polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
did not advance the parser. (#858)
- @flag/remove of an unknown/empty flag name was silent; now reports an
error like the other flag-name failure paths. (#859)
- report help said 8-hour segments but the code uses 4 (deliberately, per
4a845139f); corrected the help. (#860)
Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
|
|
|
|
// Apply the same locality/connected gate as the
|
|
|
|
|
|
// non-quoted branch; do not 'continue' here, which would
|
|
|
|
|
|
// skip the p advance below and loop forever.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (noisy_check_whisper_target(executor, target, key))
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
init_match(executor, p, NOTYPE);
|
|
|
|
|
|
match_neighbor();
|
|
|
|
|
|
target = match_result();
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (NOTHING != target)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):
- @clone/cost on an exit bypassed the "must control current location"
check (it lived only on the non-/cost path), letting a builder splice a
cloned exit into a room they do not control. (#855)
- The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
which does not exist (produces "Huh?"); corrected to @mark_all/clear.
(#856)
- @ps <object> reported nothing for a controlled object owned by another
player; do_ps was missing the non-player-target else clause that the
sibling @halt has (clear the owner filter). (#857)
- whisper "<quoted name>" skipped the locality/connected gate the unquoted
form applies, giving a success confirmation plus a delivery error and
polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
did not advance the parser. (#858)
- @flag/remove of an unknown/empty flag name was silent; now reports an
error like the other flag-name failure paths. (#859)
- report help said 8-hour segments but the code uses 4 (deliberately, per
4a845139f); corrected the help. (#860)
Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
|
|
|
|
if (noisy_check_whisper_target(executor, target, key))
|
2008-04-24 07:23:21 -07:00
|
|
|
|
{
|
|
|
|
|
|
aPlayers[nPlayers++] = target;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (q)
|
|
|
|
|
|
{
|
|
|
|
|
|
p = q + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int nValid = nPlayers;
|
|
|
|
|
|
|
|
|
|
|
|
// Remove duplicate dbrefs.
|
|
|
|
|
|
//
|
|
|
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < nPlayers-1; i++)
|
|
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (NOTHING != aPlayers[i])
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
int j;
|
|
|
|
|
|
for (j = i+1; j < nPlayers; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (aPlayers[j] == aPlayers[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
aPlayers[j] = NOTHING;
|
|
|
|
|
|
bModified = true;
|
|
|
|
|
|
nValid--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2007-08-21 20:35:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( bModified
|
2026-03-17 10:31:07 -06:00
|
|
|
|
&& 0 < nPlayers)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
// Our aPlayers could be different than the one encoded on A_LASTWHISPER
|
2007-08-18 01:53:56 +00:00
|
|
|
|
// Update the database.
|
|
|
|
|
|
//
|
|
|
|
|
|
ITL itl;
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf pBuff = LBuf_Src("do_pemit_whisper.lastwhisper");
|
2007-08-18 01:53:56 +00:00
|
|
|
|
UTF8 *pBufc = pBuff;
|
|
|
|
|
|
ItemToList_Init(&itl, pBuff, &pBufc);
|
|
|
|
|
|
for (int i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( Good_obj(aPlayers[i])
|
|
|
|
|
|
&& !ItemToList_AddInteger(&itl, aPlayers[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ItemToList_Final(&itl);
|
|
|
|
|
|
|
|
|
|
|
|
atr_add_raw(executor, A_LASTWHISPER, pBuff);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Decode PEMIT_HERE, PEMIT_ROOM, PEMIT_HTML and remove from key.
|
|
|
|
|
|
//
|
|
|
|
|
|
int mask = PEMIT_HERE | PEMIT_ROOM | PEMIT_HTML;
|
|
|
|
|
|
int pemit_flags = key & mask;
|
|
|
|
|
|
key &= ~mask;
|
|
|
|
|
|
|
|
|
|
|
|
int chPoseType = *message;
|
2026-03-04 11:37:35 -07:00
|
|
|
|
bool bPoseSpace = true;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (':' == chPoseType)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
message[0] = ' ';
|
2026-03-04 11:37:35 -07:00
|
|
|
|
if (' ' == message[1])
|
|
|
|
|
|
{
|
|
|
|
|
|
// ": test" → skip the extra space, no space between name and text.
|
|
|
|
|
|
//
|
|
|
|
|
|
bPoseSpace = false;
|
|
|
|
|
|
}
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if ( 1 == nPlayers
|
|
|
|
|
|
&& Good_obj(aPlayers[0]))
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:57:27 -07:00
|
|
|
|
switch (chPoseType)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:57:27 -07:00
|
|
|
|
case ';':
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s senses “%s%s”"),
|
2008-01-15 09:20:24 -08:00
|
|
|
|
Moniker(aPlayers[0]), Moniker(executor), &message[1]));
|
2007-08-21 20:57:27 -07:00
|
|
|
|
break;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
|
2007-08-21 20:57:27 -07:00
|
|
|
|
case ':':
|
2026-03-04 11:37:35 -07:00
|
|
|
|
if (bPoseSpace)
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s senses “%s %s”"),
|
2026-03-04 11:37:35 -07:00
|
|
|
|
Moniker(aPlayers[0]), Moniker(executor), &message[1]));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s senses “%s%s”"),
|
2026-03-04 11:37:35 -07:00
|
|
|
|
Moniker(aPlayers[0]), Moniker(executor), &message[1]));
|
|
|
|
|
|
}
|
2007-08-21 20:57:27 -07:00
|
|
|
|
break;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
|
2007-08-21 20:57:27 -07:00
|
|
|
|
default:
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("You whisper “%s” to %s."), message,
|
2007-08-21 20:57:27 -07:00
|
|
|
|
Moniker(aPlayers[0])));
|
|
|
|
|
|
break;
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2007-08-21 20:35:51 -07:00
|
|
|
|
else if (1 < nPlayers)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
LBuf aFriendly = LBuf_Src("do_pemit_whisper.friendly");
|
2007-08-18 01:53:56 +00:00
|
|
|
|
UTF8 *pFriendly = aFriendly;
|
|
|
|
|
|
|
|
|
|
|
|
bool bFirst = true;
|
|
|
|
|
|
for (int i = 0; i < nPlayers; i++)
|
|
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (NOTHING != aPlayers[i])
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (bFirst)
|
|
|
|
|
|
{
|
|
|
|
|
|
bFirst = false;
|
|
|
|
|
|
}
|
2007-09-26 12:04:34 -07:00
|
|
|
|
else if (nPlayers-1 == i)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (2 == nPlayers)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
safe_copy_buf(T(" and "), 5, aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_copy_buf(T(", and "), 6, aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
safe_copy_buf(T(", "), 2, aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
safe_str(Moniker(aPlayers[i]), aFriendly, &pFriendly);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*pFriendly = '\0';
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
switch (chPoseType)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:57:27 -07:00
|
|
|
|
case ';':
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s sense “%s%s”"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), Moniker(executor), &message[1]));
|
2007-08-21 20:57:27 -07:00
|
|
|
|
break;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
|
2007-08-21 20:57:27 -07:00
|
|
|
|
case ':':
|
2026-03-04 11:37:35 -07:00
|
|
|
|
if (bPoseSpace)
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s sense “%s %s”"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), Moniker(executor), &message[1]));
|
2026-03-04 11:37:35 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("%s sense “%s%s”"),
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get(), Moniker(executor), &message[1]));
|
2026-03-04 11:37:35 -07:00
|
|
|
|
}
|
2007-08-21 20:57:27 -07:00
|
|
|
|
break;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
|
2007-08-21 20:57:27 -07:00
|
|
|
|
default:
|
nls: format-string slices for look, speech, and player (#1419)
Continue Phase 3 after create/set/wiz: mark player/staff tprintf and
safe_tprintf format templates with M_() so whole sentences (look examine
fields, say/page/whisper, login/alias/protect notices) enter the catalogue.
Left as T(): softcode decompile templates (@create/@dig/@lock…), ANSI color
attribute assemblies, pure name+message glue ("%s %s"/"%s%s"), machine
storage ("#%d", logindata, XX hash), and log_printf diagnostics.
Regenerate pot/xx/mo without empty msgstr. Format and NLS guards green.
2026-07-27 16:02:51 +00:00
|
|
|
|
notify(executor, tprintf(M_("You whisper “%s” to %s."), message,
|
Migrate ~55 alloc_lbuf/free_lbuf sites in large engine files to LBuf RAII
Fourth wave: ast (11), funceval (10), funceval2 (7), comsys (3),
speech (10), set (6), engine (7), command (10). Covers the NOEVAL
handlers (cand/cor/if/switch/iter), function evaluators (ifelse,
letq, objeval, sortby, munge, while, sandbox), notify_check message
buffers, and command dispatch paths. Buffers stored in fargs[]
arrays, returned through output pointers, or used in ping-pong swap
patterns are left manual.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:49 -06:00
|
|
|
|
aFriendly.get()));
|
2007-08-21 20:57:27 -07:00
|
|
|
|
break;
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
for (int i = 0; i < nPlayers; i++)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (Good_obj(aPlayers[i]))
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
2007-09-26 12:04:34 -07:00
|
|
|
|
do_pemit_single(executor, key, false, pemit_flags,
|
2008-12-27 18:37:01 -08:00
|
|
|
|
tprintf(T("#%d"), aPlayers[i]), chPoseType, message);
|
2007-08-18 01:53:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
void do_pemit
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
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 *recipient,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *message,
|
|
|
|
|
|
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
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if ( nargs < 2
|
|
|
|
|
|
&& key != PEMIT_WHISPER)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (PEMIT_WHISPER == key)
|
2007-08-18 01:53:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
do_pemit_whisper(executor, caller, enactor, key, nargs, recipient, message);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-08-21 20:57:27 -07:00
|
|
|
|
// Decode PEMIT_CONTENTS and PEMIT_LIST and remove from key.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
|
|
|
|
|
bool bDoContents = false;
|
|
|
|
|
|
if (key & PEMIT_CONTENTS)
|
|
|
|
|
|
{
|
|
|
|
|
|
bDoContents = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
bool bDoList = false;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if (key & (PEMIT_LIST|PEMIT_WHISPER))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
bDoList = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
key &= ~(PEMIT_CONTENTS | PEMIT_LIST);
|
|
|
|
|
|
|
|
|
|
|
|
// Decode PEMIT_HERE, PEMIT_ROOM, PEMIT_HTML and remove from key.
|
|
|
|
|
|
//
|
|
|
|
|
|
int mask = PEMIT_HERE | PEMIT_ROOM | PEMIT_HTML;
|
|
|
|
|
|
int pemit_flags = key & mask;
|
|
|
|
|
|
key &= ~mask;
|
|
|
|
|
|
|
|
|
|
|
|
int chPoseType = *message;
|
2007-08-21 20:35:51 -07:00
|
|
|
|
if ( PEMIT_WHISPER == key
|
|
|
|
|
|
&& ':' == chPoseType)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
message[0] = ' ';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bDoList)
|
|
|
|
|
|
{
|
|
|
|
|
|
do_pemit_list(executor, key, bDoContents, pemit_flags, recipient,
|
|
|
|
|
|
chPoseType, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
do_pemit_single(executor, key, bDoContents, pemit_flags, recipient,
|
|
|
|
|
|
chPoseType, message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|