2007-01-03 03:11:53 +00:00
|
|
|
|
/*! \file set.cpp
|
|
|
|
|
|
* \brief Commands which modify objects.
|
|
|
|
|
|
*
|
|
|
|
|
|
* These functions primarily implement commands like \@set and
|
|
|
|
|
|
* \@name that modify attributes or basic properties of an object,
|
|
|
|
|
|
* but this file also includes \@trigger and the use command.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#include "copyright.h"
|
|
|
|
|
|
#include "autoconf.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include "externs.h"
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
#include "routing.h"
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
void set_modified(dbref thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeAbsolute ltaNow;
|
|
|
|
|
|
ltaNow.GetLocal();
|
|
|
|
|
|
atr_add_raw(thing, A_MODIFIED, ltaNow.ReturnDateString(7));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
dbref match_controlled_handler(dbref executor, const UTF8 *name, bool bQuiet)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref mat;
|
|
|
|
|
|
init_match(executor, name, NOTYPE);
|
|
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
if (bQuiet)
|
|
|
|
|
|
{
|
|
|
|
|
|
mat = match_result();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
mat = noisy_match_result();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Good_obj(mat))
|
|
|
|
|
|
{
|
|
|
|
|
|
return mat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Controls(executor, mat))
|
|
|
|
|
|
{
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
if (NoModify(mat) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!bQuiet)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return mat;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!bQuiet)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_chzone
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *newobj,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (!mudconf.have_zones)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("Zones disabled."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
init_match(executor, name, NOTYPE);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
dbref thing = noisy_match_result();
|
|
|
|
|
|
if (thing == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref zone;
|
|
|
|
|
|
if ( newobj[0] == '\0'
|
2007-03-14 22:33:04 +00:00
|
|
|
|
|| !mux_stricmp(newobj, T("none")))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
zone = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
init_match(executor, newobj, NOTYPE);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
zone = noisy_match_result();
|
|
|
|
|
|
if (zone == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !isThing(zone)
|
|
|
|
|
|
&& !isRoom(zone))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("Invalid zone object type."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( !Wizard(executor)
|
|
|
|
|
|
&& !Controls(executor, thing)
|
|
|
|
|
|
&& !check_zone_handler(executor, thing, true)
|
|
|
|
|
|
&& db[executor].owner != db[thing].owner)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("You don’t have the power to shift reality."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
if (NoModify(thing) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// A player may change an object's zone to NOTHING or to an object he owns.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( zone != NOTHING
|
|
|
|
|
|
&& !Wizard(executor)
|
|
|
|
|
|
&& !Controls(executor, zone)
|
|
|
|
|
|
&& db[executor].owner != db[zone].owner)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("You cannot move that object to that zone."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Only rooms may be zoned to other rooms.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( zone != NOTHING
|
|
|
|
|
|
&& isRoom(zone)
|
|
|
|
|
|
&& !isRoom(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("Only rooms may be zoned to other rooms."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Everything is okay, do the change.
|
|
|
|
|
|
//
|
2026-03-29 03:53:13 -06:00
|
|
|
|
dbref old_zone = Zone(thing);
|
2026-03-04 23:16:08 -07:00
|
|
|
|
s_Zone(thing, zone);
|
2026-03-29 03:53:13 -06:00
|
|
|
|
|
|
|
|
|
|
// If a room changed zones, invalidate both the old and new zone
|
|
|
|
|
|
// routing tables plus the inter-zone meta-table.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (isRoom(thing) && Navigable(thing) && old_zone != zone)
|
|
|
|
|
|
{
|
|
|
|
|
|
route_invalidate_zone(old_zone);
|
|
|
|
|
|
route_invalidate_zone(zone);
|
|
|
|
|
|
route_invalidate_meta();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!isPlayer(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// If the object is a player, resetting these flags is rather
|
|
|
|
|
|
// inconvenient -- although this may pose a bit of a security risk. Be
|
|
|
|
|
|
// careful when @chzone'ing wizard or royal players.
|
|
|
|
|
|
//
|
2018-10-03 17:54:51 +00:00
|
|
|
|
SetClearFlags(thing, mudconf.stripped_flags.word, nullptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Wipe out all powers.
|
|
|
|
|
|
//
|
|
|
|
|
|
Powers(thing) = 0;
|
2006-11-28 21:40:35 +00:00
|
|
|
|
Powers2(thing) = 0;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify(executor, M_("Zone changed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_name
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *newname,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing = match_controlled(executor, name);
|
2008-06-22 15:59:48 -07:00
|
|
|
|
if (NOTHING == thing)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for bad name.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nargs < 2
|
2008-06-22 15:59:48 -07:00
|
|
|
|
|| '\0' == newname[0])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Give it what new name?"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for renaming a player.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (isPlayer(thing))
|
|
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *buff = trim_spaces(newname);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( !ValidatePlayerName(buff)
|
|
|
|
|
|
|| !badname_check(buff))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You can’t use that name."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
Feature: Add MSSP, GMCP, @protect, and benchmark()
Four new features identified from the TinyMUSH/PennMUSH/RhostMUSH survey:
MSSP (MUD Server Status Protocol, telnet option 70):
- Server sends structured key-value data (NAME, PLAYERS, UPTIME, PORT,
CODEBASE, FAMILY) to MU* directory crawlers on IAC DO MSSP
- Stateless response via send_mssp() in telnet.cpp using g_dc config basket
- start_time_utc added to DRIVER_CONFIG for uptime calculation
GMCP (Generic MUD Communication Protocol, telnet option 201):
- Protocol negotiation: server offers WILL GMCP, tracks gmcp_enabled per DESC
- Inbound: GMCP subneg queued as synthetic "\x01GMCP" command, dispatched
to handle_gmcp() which fires A_GMCP attribute with %0=package %1=json
- Outbound: gmcp(<player>, <package>, <json>) softcode function sends
GMCP frames to all GMCP-enabled descriptors via SendGmcp COM method
- Full COM architecture: mux_IConnectionManager::SendGmcp in driver,
send_gmcp() bridge in engine, CConnectionManager impl in modules.cpp
@protect (player name reservation):
- @protect[/add] <name>, @protect/del <name>, @protect/list [<player>]
- A_PROTECTNAME attribute (234) stores space-separated protected names
- protectname_check() hooked into create_player() and do_name()
- max_name_protect config param (default 5)
benchmark(<expression>, <iterations>):
- FN_NOEVAL, CA_PUBLIC, 10000 iteration cap
- Uses clock_gettime(CLOCK_MONOTONIC) / QueryPerformanceCounter
- Returns elapsed seconds as floating point
Also fixes: add unicode_tables.c to libmux.so LIBMUX_C_SRC (resolves
pre-existing tr_tolower_sbt etc. link errors).
505/505 smoke tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:08:55 -06:00
|
|
|
|
if (!protectname_check(buff, thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That name is protected by another player."));
|
Feature: Add MSSP, GMCP, @protect, and benchmark()
Four new features identified from the TinyMUSH/PennMUSH/RhostMUSH survey:
MSSP (MUD Server Status Protocol, telnet option 70):
- Server sends structured key-value data (NAME, PLAYERS, UPTIME, PORT,
CODEBASE, FAMILY) to MU* directory crawlers on IAC DO MSSP
- Stateless response via send_mssp() in telnet.cpp using g_dc config basket
- start_time_utc added to DRIVER_CONFIG for uptime calculation
GMCP (Generic MUD Communication Protocol, telnet option 201):
- Protocol negotiation: server offers WILL GMCP, tracks gmcp_enabled per DESC
- Inbound: GMCP subneg queued as synthetic "\x01GMCP" command, dispatched
to handle_gmcp() which fires A_GMCP attribute with %0=package %1=json
- Outbound: gmcp(<player>, <package>, <json>) softcode function sends
GMCP frames to all GMCP-enabled descriptors via SendGmcp COM method
- Full COM architecture: mux_IConnectionManager::SendGmcp in driver,
send_gmcp() bridge in engine, CConnectionManager impl in modules.cpp
@protect (player name reservation):
- @protect[/add] <name>, @protect/del <name>, @protect/list [<player>]
- A_PROTECTNAME attribute (234) stores space-separated protected names
- protectname_check() hooked into create_player() and do_name()
- max_name_protect config param (default 5)
benchmark(<expression>, <iterations>):
- FN_NOEVAL, CA_PUBLIC, 10000 iteration cap
- Uses clock_gettime(CLOCK_MONOTONIC) / QueryPerformanceCounter
- Returns elapsed seconds as floating point
Also fixes: add unicode_tables.c to libmux.so LIBMUX_C_SRC (resolves
pre-existing tr_tolower_sbt etc. link errors).
505/505 smoke tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:08:55 -06:00
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2007-12-12 10:53:01 -08:00
|
|
|
|
else
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2008-06-22 15:59:48 -07:00
|
|
|
|
bool bAlias = false;
|
|
|
|
|
|
dbref jwho = lookup_player_name(buff, bAlias);
|
|
|
|
|
|
if ( NOTHING != jwho
|
|
|
|
|
|
&& ( jwho != thing
|
|
|
|
|
|
|| bAlias))
|
2007-12-12 10:53:01 -08:00
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That name is already in use."));
|
2007-12-12 10:53:01 -08:00
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Everything ok, notify.
|
|
|
|
|
|
//
|
|
|
|
|
|
STARTLOG(LOG_SECURITY, "SEC", "CNAME");
|
|
|
|
|
|
log_name(thing),
|
2007-03-14 22:33:04 +00:00
|
|
|
|
log_text(T(" renamed to "));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
log_text(buff);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
if (Suspect(thing))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
raw_broadcast(WIZARD, M_("[Suspect] %s renamed to %s"), Name(thing), buff);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2008-06-22 15:59:48 -07:00
|
|
|
|
delete_player_name(thing, Name(thing), false);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
s_Name(thing, buff);
|
|
|
|
|
|
set_modified(thing);
|
2008-06-22 15:59:48 -07:00
|
|
|
|
add_player_name(thing, Name(thing), false);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!Quiet(executor) && !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Name set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t nValidName;
|
|
|
|
|
|
bool bValid;
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *pValidName;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (isExit(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
pValidName = MakeCanonicalExitName(newname, &nValidName, &bValid);
|
|
|
|
|
|
}
|
2007-12-21 17:46:20 -08:00
|
|
|
|
else if (isThing(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.thing_name_charset);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-12-21 17:46:20 -08:00
|
|
|
|
pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.room_name_charset);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!bValid)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That is not a reasonable name."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Everything ok, change the name.
|
|
|
|
|
|
//
|
|
|
|
|
|
s_Name(thing, pValidName);
|
|
|
|
|
|
set_modified(thing);
|
|
|
|
|
|
if (!Quiet(executor) && !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Name set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_alias: Make an alias for a player or object.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void do_alias
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *alias,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing = match_controlled(executor, name);
|
2008-06-22 15:59:48 -07:00
|
|
|
|
if (NOTHING == thing)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for renaming a player.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
ATTR *ap = atr_num(A_ALIAS);
|
|
|
|
|
|
if (isPlayer(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Fetch the old alias.
|
|
|
|
|
|
//
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf oldalias = LBuf_Adopt(atr_pget(thing, A_ALIAS, &aowner, &aflags));
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *trimalias = trim_spaces(alias);
|
2008-03-19 02:34:44 +00:00
|
|
|
|
dbref nPlayer;
|
2008-06-22 15:59:48 -07:00
|
|
|
|
bool bAlias = false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (!Controls(executor, thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Make sure we have rights to do it. We can't do the
|
|
|
|
|
|
// normal Set_attr check because ALIAS is only
|
|
|
|
|
|
// writable by GOD and we want to keep people from
|
|
|
|
|
|
// doing &ALIAS and bypassing the player name checks.
|
|
|
|
|
|
//
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!*trimalias)
|
|
|
|
|
|
{
|
|
|
|
|
|
// New alias is null, just clear it.
|
|
|
|
|
|
//
|
2008-06-22 15:59:48 -07:00
|
|
|
|
delete_player_name(thing, oldalias, true);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_clr(thing, A_ALIAS);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Alias removed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-06-22 15:59:48 -07:00
|
|
|
|
else if ( (nPlayer = lookup_player_name(trimalias, bAlias)) != NOTHING
|
|
|
|
|
|
&& ( nPlayer != thing
|
|
|
|
|
|
|| !bAlias))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Make sure new alias isn't already in use.
|
|
|
|
|
|
//
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That name is already in use."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2008-05-16 15:25:12 -07:00
|
|
|
|
else if ( !( badname_check(trimalias)
|
|
|
|
|
|
&& ValidatePlayerName(trimalias)))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That’s a silly name for a player!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove the old name and add the new name.
|
|
|
|
|
|
//
|
2008-06-22 15:59:48 -07:00
|
|
|
|
delete_player_name(thing, oldalias, true);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_add(thing, A_ALIAS, trimalias, Owner(executor), aflags);
|
2008-06-22 15:59:48 -07:00
|
|
|
|
if (add_player_name(thing, trimalias, true))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Alias set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor,
|
2007-03-14 22:33:04 +00:00
|
|
|
|
T("That name is already in use or is illegal, alias cleared."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_clr(thing, A_ALIAS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(trimalias);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_pget_info(thing, A_ALIAS, &aowner, &aflags);
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure we have rights to do it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!bCanSetAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_add(thing, A_ALIAS, alias, Owner(executor), aflags);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_forwardlist: Set a forwardlist.
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
void do_forwardlist
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *target,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *newlist,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing = match_controlled(executor, target);
|
|
|
|
|
|
if (thing == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
dbref aowner, aflags;
|
|
|
|
|
|
atr_pget_info(thing, A_FORWARDLIST, &aowner, &aflags);
|
|
|
|
|
|
|
|
|
|
|
|
if (!Controls(executor, thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!*newlist)
|
|
|
|
|
|
{
|
|
|
|
|
|
// New forwardlist is null, just clear it.
|
|
|
|
|
|
//
|
|
|
|
|
|
atr_clr(thing, A_FORWARDLIST);
|
|
|
|
|
|
set_modified(thing);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Forwardlist removed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!fwdlist_ck(executor, thing, A_FORWARDLIST, newlist))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Invalid forwardlist."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_add(thing, A_FORWARDLIST, newlist, Owner(executor), aflags);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_lock: Set a lock on an object or attribute.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void do_lock
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *keytext,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
|
|
|
|
|
|
if ( parse_attrib(executor, name, &thing, &ap)
|
|
|
|
|
|
&& ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute not present on object."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bCanLockAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
aflags |= AF_LOCK;
|
|
|
|
|
|
atr_set_flags(thing, ap->number, aflags);
|
|
|
|
|
|
if ( !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute locked."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
init_match(executor, name, NOTYPE);
|
|
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
thing = match_result();
|
|
|
|
|
|
|
|
|
|
|
|
switch (thing)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NOTHING:
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t see what you want to lock!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
case AMBIGUOUS:
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t know which one you want to lock!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
if (!Controls(executor, thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You can’t lock that!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
if (NoModify(thing) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
Convert all static scratch buffers to thread_local
43 static scratch-buffer arrays across 21 files (5 in mux/src, 38 in
mux/modules/) changed from `static` to `thread_local`. Under the
current single-threaded evaluator this is a zero-behavior-change swap
— `thread_local` storage has the same lifetime and zero-allocation
properties as `static` — but each thread gets its own copy, which
makes these functions safe for a future multi-threaded evaluator
without any locking.
Read-only constant tables (`aRadix64`, `aRadixPenn36`,
`aRadixPenn64`, `Empty`) left as `static` because they are immutable
shared data.
Affected areas:
net.cpp — queue_string co_buf, trimmed_site, dump_users NameField
signals.cpp — signal_desc
stubslave.cpp — Stub_PipePump
attrcache.cpp — sqlite_attr_buf
boolexp.cpp — parsestore
command.cpp — preserve_cmd, SpaceCompressCommand, LowerCaseCommand
comsys.cpp — NewTitle, Buffer, temp
db.cpp — tbuff, Buffer (x2)
flags.cpp — buff
funceval.cpp — textbuff
functions.cpp — TimeBuffer64, TimeBuffer80, Buffer
help.cpp — Line, Buffer
mail.cpp — aFolders, Buffer, res, szFittedMailAliasDesc
match.cpp — buffer
player.cpp — szSalt, buf (x2), buff
plusemail.cpp — buf
predicates.cpp — Buf (x2), pName
session.cpp — szFittedDoing
set.cpp — pRestrictedKeyText
unparse.cpp — buf, boolexp_buf
mail_mod.cpp — result, res, buf
All 21 files verified with g++ -std=c++17 -fsyntax-only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:21:14 -06:00
|
|
|
|
thread_local UTF8 pRestrictedKeyText[LBUF_SIZE];
|
2007-03-21 02:39:49 +00:00
|
|
|
|
StripTabsAndTruncate(keytext, pRestrictedKeyText, LBUF_SIZE-1, LBUF_SIZE-1);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
struct boolexp *okey = parse_boolexp(executor, pRestrictedKeyText, false);
|
|
|
|
|
|
if (okey == TRUE_BOOLEXP)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t understand that key."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Everything ok, do it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!key)
|
|
|
|
|
|
{
|
|
|
|
|
|
key = A_LOCK;
|
|
|
|
|
|
}
|
|
|
|
|
|
atr_add_raw(thing, key, unparse_boolexp_quiet(executor, okey));
|
|
|
|
|
|
if ( !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Locked."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
free_boolexp(okey);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * Remove a lock from an object of attribute.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_unlock(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
|
|
|
|
|
|
if ( parse_attrib(executor, name, &thing, &ap)
|
|
|
|
|
|
&& ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
// We have been asked to unlock an attribute.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute not present on object."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bCanLockAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
aflags &= ~AF_LOCK;
|
|
|
|
|
|
atr_set_flags(thing, ap->number, aflags);
|
|
|
|
|
|
if ( !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute unlocked."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We have been asked to change the ownership of an object.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!key)
|
|
|
|
|
|
{
|
|
|
|
|
|
key = A_LOCK;
|
|
|
|
|
|
}
|
|
|
|
|
|
thing = match_controlled(executor, name);
|
|
|
|
|
|
if (thing != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_clr(thing, key);
|
|
|
|
|
|
set_modified(thing);
|
|
|
|
|
|
if (!Quiet(executor) && !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Unlocked."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_unlink: Unlink an exit from its destination or remove a dropto.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_unlink(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
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
|
|
|
|
|
|
|
|
|
|
dbref exit;
|
|
|
|
|
|
|
|
|
|
|
|
init_match(executor, name, TYPE_EXIT);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
exit = match_result();
|
|
|
|
|
|
|
|
|
|
|
|
switch (exit)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NOTHING:
|
|
|
|
|
|
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Unlink what?"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case AMBIGUOUS:
|
|
|
|
|
|
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t know which one you mean!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
|
|
if (!Controls(executor, exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
else if (NoModify(exit) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (Typeof(exit))
|
|
|
|
|
|
{
|
|
|
|
|
|
case TYPE_EXIT:
|
|
|
|
|
|
|
|
|
|
|
|
s_Location(exit, NOTHING);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Unlinked."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
giveto(Owner(exit), mudconf.linkcost);
|
Implement Phase 1 routing: static unconditional next-hop tables
Add route() softcode function with BFS-based shortest-path routing
over rooms marked NAVIGABLE. The routing table stores only the next-hop
exit for each (source, dest) pair, compressed via diagonal elimination,
adjacent marking, and row redundancy. Lazy rebuild on generation-counter
mismatch triggered by topology changes (@dig, @destroy, @link, @open,
@unlink) and NAVIGABLE flag changes. SQLite schema v10 adds route_nodes,
route_table, route_meta tables for future persistence phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 03:18:37 -06:00
|
|
|
|
route_invalidate();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TYPE_ROOM:
|
|
|
|
|
|
|
|
|
|
|
|
s_Dropto(exit, NOTHING);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Dropto removed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You can’t unlink that!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-11-29 17:32:09 +00:00
|
|
|
|
void TranslateFlags_Clone
|
|
|
|
|
|
(
|
|
|
|
|
|
FLAG aClearFlags[3],
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
int key
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (key & CLONE_NOSTRIP)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
// #1 using /nostrip causes nothing to be stripped.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// With #1 powers, /nostrip still strips the WIZARD bit.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = WIZARD;
|
|
|
|
|
|
}
|
|
|
|
|
|
aClearFlags[FLAG_WORD2] = 0;
|
|
|
|
|
|
aClearFlags[FLAG_WORD3] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( (key & CLONE_INHERIT)
|
|
|
|
|
|
&& Inherits(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
// The /inherit switch specifically allows INHERIT powers through.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = (WIZARD | mudconf.stripped_flags.word[FLAG_WORD1]) & ~(INHERIT);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Normally WIZARD and the other stripped_flags are removed.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = WIZARD | mudconf.stripped_flags.word[FLAG_WORD1];
|
|
|
|
|
|
}
|
|
|
|
|
|
aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2];
|
|
|
|
|
|
aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TranslateFlags_Chown
|
2006-11-29 08:22:07 +00:00
|
|
|
|
(
|
|
|
|
|
|
FLAG aClearFlags[3],
|
|
|
|
|
|
FLAG aSetFlags[3],
|
|
|
|
|
|
bool *bClearPowers,
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
int key
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (key & CHOWN_NOSTRIP)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
// #1 using /nostrip only clears CHOWN_OK and sets HALT.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = CHOWN_OK;
|
|
|
|
|
|
*bClearPowers = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// With /nostrip, CHOWN_OK and WIZARD are cleared, HALT is
|
|
|
|
|
|
// set, and powers are cleared.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD;
|
|
|
|
|
|
*bClearPowers = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
aClearFlags[FLAG_WORD2] = 0;
|
|
|
|
|
|
aClearFlags[FLAG_WORD3] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Without /nostrip, CHOWN_OK, WIZARD, and stripped_flags are
|
|
|
|
|
|
// cleared, HALT is set, powers are cleared.
|
|
|
|
|
|
//
|
|
|
|
|
|
aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD
|
|
|
|
|
|
| mudconf.stripped_flags.word[FLAG_WORD1];
|
|
|
|
|
|
aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2];
|
|
|
|
|
|
aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3];
|
|
|
|
|
|
*bClearPowers = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
aSetFlags[FLAG_WORD1] = HALT;
|
|
|
|
|
|
aSetFlags[FLAG_WORD2] = 0;
|
|
|
|
|
|
aSetFlags[FLAG_WORD3] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-11-29 08:26:01 +00:00
|
|
|
|
void SetClearFlags
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref thing,
|
|
|
|
|
|
FLAG aClearFlags[3],
|
|
|
|
|
|
FLAG aSetFlags[3]
|
|
|
|
|
|
)
|
2006-11-29 08:22:07 +00:00
|
|
|
|
{
|
|
|
|
|
|
int j;
|
|
|
|
|
|
for (j = FLAG_WORD1; j <= FLAG_WORD3; j++)
|
|
|
|
|
|
{
|
2026-03-04 23:16:08 -07:00
|
|
|
|
FLAG newval = db[thing].fs.word[j];
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != aClearFlags)
|
2006-11-29 08:22:07 +00:00
|
|
|
|
{
|
2026-03-04 23:16:08 -07:00
|
|
|
|
newval &= ~aClearFlags[j];
|
2006-11-29 08:22:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != aSetFlags)
|
2006-11-29 08:22:07 +00:00
|
|
|
|
{
|
2026-03-04 23:16:08 -07:00
|
|
|
|
newval |= aSetFlags[j];
|
2006-11-29 08:22:07 +00:00
|
|
|
|
}
|
2026-03-04 23:16:08 -07:00
|
|
|
|
s_Flags(thing, j, newval);
|
2006-11-29 08:22:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_chown: Change ownership of an object or attribute.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void do_chown
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *newown,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref nOwnerOrig, nOwnerNew, thing;
|
|
|
|
|
|
bool bDoit;
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
|
|
|
|
|
|
if ( parse_attrib(executor, name, &thing, &ap)
|
|
|
|
|
|
&& ap
|
|
|
|
|
|
&& See_attr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
// An attribute was given, so we worry about changing the owner of the
|
|
|
|
|
|
// attribute.
|
|
|
|
|
|
//
|
|
|
|
|
|
nOwnerOrig = Owner(thing);
|
|
|
|
|
|
if (!*newown)
|
|
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = nOwnerOrig;
|
|
|
|
|
|
}
|
2007-03-14 22:33:04 +00:00
|
|
|
|
else if (!string_compare(newown, T("me")))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = Owner(executor);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = lookup_player(executor, newown, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// You may chown an attr to yourself if you own the object and the attr
|
|
|
|
|
|
// is not locked. You may chown an attr to the owner of the object if
|
|
|
|
|
|
// you own the attribute. To do anything else you must be a wizard.
|
|
|
|
|
|
// Only #1 can chown attributes on #1.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute not present on object."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
bDoit = false;
|
|
|
|
|
|
if (nOwnerNew == NOTHING)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I couldn’t find that player."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if ( God(thing)
|
|
|
|
|
|
&& !God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Wizard(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
bDoit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (nOwnerNew == Owner(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Chown to me: only if I own the obj and !locked
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !Controls(executor, thing)
|
|
|
|
|
|
|| (aflags & AF_LOCK))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bDoit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (nOwnerNew == nOwnerOrig)
|
|
|
|
|
|
{
|
|
|
|
|
|
// chown to obj owner: only if I own attr and !locked
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( Owner(executor) != aowner
|
|
|
|
|
|
|| (aflags & AF_LOCK))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bDoit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!bDoit)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!bCanSetAttr(executor, executor, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf buff = LBuf_Adopt(atr_get("do_chown.887", thing, ap->number, &aowner, &aflags));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
atr_add(thing, ap->number, buff, nOwnerNew, aflags);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute owner changed."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// An attribute was not specified, so we are being asked to change the
|
|
|
|
|
|
// owner of the object.
|
|
|
|
|
|
//
|
|
|
|
|
|
init_match(executor, name, TYPE_THING);
|
|
|
|
|
|
match_possession();
|
|
|
|
|
|
match_here();
|
|
|
|
|
|
match_exit();
|
|
|
|
|
|
match_me();
|
|
|
|
|
|
if (Chown_Any(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
match_player();
|
|
|
|
|
|
match_absolute();
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (thing = match_result())
|
|
|
|
|
|
{
|
|
|
|
|
|
case NOTHING:
|
|
|
|
|
|
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You don’t have that!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
case AMBIGUOUS:
|
|
|
|
|
|
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t know which you mean!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
nOwnerOrig = Owner(thing);
|
|
|
|
|
|
|
Implement NOMODIFY flag (#487).
Add NOMODIFY flag (bit 0x00008000 on FLAG_WORD3) that prevents all
modifications to an object -- attributes, flags, locks, name, owner,
zone, parent, and links -- even by its owner. Only Wizards and
Royalty bypass the restriction. Aliased as NO_MODIFY.
Checks added at 11 choke points across 5 source files:
- bCanSetAttr() and bCanLockAttr() in predicates.cpp
- flag_set() in flags.cpp
- match_controlled_handler(), do_chzone(), do_lock(), do_chown(),
do_unlink() in set.cpp
- do_parent(), link_exit(), do_link() in create.cpp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:19:57 -07:00
|
|
|
|
if (NoModify(thing) && !WizRoy(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 22:33:04 +00:00
|
|
|
|
if (!*newown || !(string_compare(newown, T("me"))))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = Owner(executor);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = lookup_player(executor, newown, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int cost = 1, quota = 1;
|
|
|
|
|
|
|
|
|
|
|
|
switch (Typeof(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
case TYPE_ROOM:
|
|
|
|
|
|
|
|
|
|
|
|
cost = mudconf.digcost;
|
|
|
|
|
|
quota = mudconf.room_quota;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TYPE_THING:
|
|
|
|
|
|
|
|
|
|
|
|
cost = OBJECT_DEPOSIT(Pennies(thing));
|
|
|
|
|
|
quota = mudconf.thing_quota;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TYPE_EXIT:
|
|
|
|
|
|
|
|
|
|
|
|
cost = mudconf.opencost;
|
|
|
|
|
|
quota = mudconf.exit_quota;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TYPE_PLAYER:
|
|
|
|
|
|
|
|
|
|
|
|
cost = mudconf.robotcost;
|
|
|
|
|
|
quota = mudconf.player_quota;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool bPlayerControlsThing = Controls(executor, thing);
|
|
|
|
|
|
if ( isGarbage(thing)
|
|
|
|
|
|
&& bPlayerControlsThing)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You shouldn’t be rummaging through the garbage."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (nOwnerNew == NOTHING)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I couldn’t find that player."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if ( isPlayer(thing)
|
|
|
|
|
|
&& !God(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Players always own themselves."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if ( ( !bPlayerControlsThing
|
|
|
|
|
|
&& !Chown_Any(executor)
|
|
|
|
|
|
&& !Chown_ok(thing))
|
|
|
|
|
|
|| ( isThing(thing)
|
|
|
|
|
|
&& Location(thing) != executor
|
|
|
|
|
|
&& !Chown_Any(executor))
|
|
|
|
|
|
|| !Controls(executor, nOwnerNew)
|
|
|
|
|
|
|| God(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (canpayfees(executor, nOwnerNew, cost, quota))
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(nOwnerOrig, cost);
|
|
|
|
|
|
if (mudconf.quotas)
|
|
|
|
|
|
{
|
|
|
|
|
|
add_quota(nOwnerOrig, quota);
|
|
|
|
|
|
}
|
2006-09-15 22:44:58 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!God(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
nOwnerNew = Owner(nOwnerNew);
|
|
|
|
|
|
}
|
2006-09-15 22:44:58 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
s_Owner(thing, nOwnerNew);
|
|
|
|
|
|
atr_chown(thing);
|
2006-09-15 22:44:58 +00:00
|
|
|
|
|
2006-11-29 08:22:07 +00:00
|
|
|
|
FLAGSET clearflags;
|
|
|
|
|
|
FLAGSET setflags;
|
|
|
|
|
|
bool bClearPowers;
|
|
|
|
|
|
|
2006-11-29 17:32:09 +00:00
|
|
|
|
TranslateFlags_Chown(clearflags.word, setflags.word, &bClearPowers, executor, key);
|
2006-11-29 08:22:07 +00:00
|
|
|
|
|
|
|
|
|
|
SetClearFlags(thing, clearflags.word, setflags.word);
|
|
|
|
|
|
if (bClearPowers)
|
2006-09-15 22:44:58 +00:00
|
|
|
|
{
|
|
|
|
|
|
s_Powers(thing, 0);
|
|
|
|
|
|
s_Powers2(thing, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Always halt the queue.
|
|
|
|
|
|
//
|
2006-09-01 22:59:23 +00:00
|
|
|
|
halt_que(NOTHING, thing);
|
2006-09-15 22:44:58 +00:00
|
|
|
|
|
|
|
|
|
|
// Warn if ROYALTY is left set.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (ROYALTY & db[thing].fs.word[FLAG_WORD1])
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor,
|
2026-07-27 15:04:54 +00:00
|
|
|
|
tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves ROYALTY privilege intact."),
|
2006-09-15 22:44:58 +00:00
|
|
|
|
Moniker(thing), thing));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Warn if INHERIT is left set.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (INHERIT & db[thing].fs.word[FLAG_WORD1])
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor,
|
2026-07-27 15:04:54 +00:00
|
|
|
|
tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves INHERIT privilege intact."),
|
2006-09-15 22:44:58 +00:00
|
|
|
|
Moniker(thing), thing));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
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_chown.notify");
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *bp = buff;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2008-12-27 18:37:01 -08:00
|
|
|
|
safe_tprintf_str(buff, &bp, T("Owner of %s(#%d) changed from "), Moniker(thing), thing);
|
|
|
|
|
|
safe_tprintf_str(buff, &bp, T("%s(#%d) to "), Moniker(nOwnerOrig), nOwnerOrig);
|
|
|
|
|
|
safe_tprintf_str(buff, &bp, T("%s(#%d)."), Moniker(nOwnerNew), nOwnerNew);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
notify_quiet(executor, buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_set: Set flags or attributes on objects, or flags on attributes.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-04-09 07:18:32 +00:00
|
|
|
|
static void set_attr_internal(dbref player, dbref thing, int attrnum, const UTF8 *attrtext, int key)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
ATTR *pattr = atr_num(attrnum);
|
|
|
|
|
|
atr_pget_info(thing, attrnum, &aowner, &aflags);
|
|
|
|
|
|
if ( pattr
|
|
|
|
|
|
&& bCanSetAttr(player, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
bool could_hear = Hearer(thing);
|
|
|
|
|
|
atr_add(thing, attrnum, attrtext, Owner(player), aflags);
|
|
|
|
|
|
handle_ears(thing, could_hear, Hearer(thing));
|
|
|
|
|
|
if ( !(key & SET_QUIET)
|
|
|
|
|
|
&& !Quiet(player)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(player, M_("Set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(player, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-04-25 16:41:26 +00:00
|
|
|
|
bool copy_attr(dbref executor, attr_info &src, attr_info &dest, int key)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !Good_obj(src.m_object)
|
|
|
|
|
|
|| !Good_obj(dest.m_object)
|
2018-10-03 17:54:51 +00:00
|
|
|
|
|| nullptr == src.m_attr)
|
2007-04-25 16:41:26 +00:00
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2007-04-25 16:41:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if ( nullptr == dest.m_attr
|
2007-04-25 16:41:26 +00:00
|
|
|
|
|| !See_attr(executor, src.m_object, src.m_attr)
|
|
|
|
|
|
|| !bCanSetAttr(executor, dest.m_object, dest.m_attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
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("copy_attr");
|
2007-04-25 16:41:26 +00:00
|
|
|
|
atr_pget_str(buff, src.m_object, src.m_attr->number, &src.m_aowner, &src.m_aflags);
|
|
|
|
|
|
src.m_bHaveInfo = true;
|
|
|
|
|
|
|
|
|
|
|
|
set_attr_internal(executor, dest.m_object, dest.m_attr->number, buff, key);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
void do_set
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *flagname,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing, aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
|
|
|
|
|
|
// See if we have the <obj>/<attr> form, which is how you set
|
|
|
|
|
|
// attribute flags.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (parse_attrib(executor, name, &thing, &pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( pattr
|
|
|
|
|
|
&& See_attr(executor, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
// You must specify a flag name.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !flagname
|
|
|
|
|
|
|| flagname[0] == '\0')
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t know what you want to set!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for clearing.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool clear = false;
|
|
|
|
|
|
if (flagname[0] == NOT_TOKEN)
|
|
|
|
|
|
{
|
|
|
|
|
|
flagname++;
|
|
|
|
|
|
clear = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure player specified a valid attribute flag.
|
|
|
|
|
|
//
|
|
|
|
|
|
int flagvalue;
|
|
|
|
|
|
if (!search_nametab(executor, indiv_attraccess_nametab, flagname, &flagvalue))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You can’t set that!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure the object has the attribute present.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!atr_get_info(thing, pattr->number, &aowner, &aflags))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Attribute not present on object."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure we can write to the attribute.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!bCanSetAttr(executor, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-03 20:21:22 -07:00
|
|
|
|
// Check if the attribute flag is already in the desired
|
|
|
|
|
|
// state.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (clear ? ((aflags & flagvalue) == 0) : ((aflags & flagvalue) == flagvalue))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( !(key & SET_QUIET)
|
|
|
|
|
|
&& !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (clear)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Already cleared."));
|
2026-03-03 20:21:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Already set."));
|
2026-03-03 20:21:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Go do it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (clear)
|
|
|
|
|
|
{
|
|
|
|
|
|
aflags &= ~flagvalue;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
aflags |= flagvalue;
|
|
|
|
|
|
}
|
|
|
|
|
|
bool could_hear = Hearer(thing);
|
|
|
|
|
|
|
|
|
|
|
|
atr_set_flags(thing, pattr->number, aflags);
|
|
|
|
|
|
|
|
|
|
|
|
// Tell the player about it.
|
|
|
|
|
|
//
|
|
|
|
|
|
handle_ears(thing, could_hear, Hearer(thing));
|
|
|
|
|
|
|
|
|
|
|
|
if ( !(key & SET_QUIET)
|
|
|
|
|
|
&& !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (clear)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Cleared."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Set."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find thing.
|
|
|
|
|
|
//
|
|
|
|
|
|
thing = match_controlled(executor, name);
|
|
|
|
|
|
if (!Good_obj(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for attribute set first.
|
|
|
|
|
|
//
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *p;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for (p = flagname; *p && (*p != ':'); p++)
|
|
|
|
|
|
{
|
|
|
|
|
|
; // Nothing.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*p)
|
|
|
|
|
|
{
|
2017-07-15 20:23:45 +00:00
|
|
|
|
*p++ = '\0';
|
2007-03-14 19:32:44 +00:00
|
|
|
|
int atr = mkattr(executor, flagname);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (atr <= 0)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Couldn’t create attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
pattr = atr_num(atr);
|
|
|
|
|
|
if (!pattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!bCanSetAttr(executor, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for _
|
|
|
|
|
|
//
|
|
|
|
|
|
if (*p == '_')
|
|
|
|
|
|
{
|
2007-04-09 07:18:32 +00:00
|
|
|
|
p++;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2017-07-15 20:23:45 +00:00
|
|
|
|
attr_info src(executor, p, false, false);
|
|
|
|
|
|
attr_info dest(thing, pattr);
|
2007-04-26 20:57:47 +00:00
|
|
|
|
copy_attr(executor, src, dest, key);
|
2007-04-09 07:18:32 +00:00
|
|
|
|
return;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Go set it.
|
|
|
|
|
|
//
|
|
|
|
|
|
set_attr_internal(executor, thing, atr, p, key);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set or clear a flag.
|
|
|
|
|
|
//
|
|
|
|
|
|
flag_set(thing, executor, flagname, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_power
|
|
|
|
|
|
(
|
|
|
|
|
|
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 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *flag,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( !flag
|
|
|
|
|
|
|| !*flag)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("I don’t know what you want to set!"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find thing.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref thing = match_controlled(executor, name);
|
|
|
|
|
|
if (thing == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
power_set(thing, executor, flag, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void do_setattr
|
|
|
|
|
|
(
|
|
|
|
|
|
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 attrnum,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *name,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *attrtext,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
init_match(executor, name, NOTYPE);
|
|
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
dbref thing = noisy_match_result();
|
|
|
|
|
|
|
|
|
|
|
|
if (!Good_obj(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
set_attr_internal(executor, thing, attrnum, attrtext, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-12-27 17:53:42 +00:00
|
|
|
|
void do_cpattr(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UTF8 *oldpair, UTF8 *newpair[], int nargs, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-05-27 19:30:16 +00:00
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
2006-12-27 17:53:42 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-04-25 16:41:26 +00:00
|
|
|
|
if ( isEmpty(oldpair)
|
|
|
|
|
|
|| isEmpty(newpair[0])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| nargs < 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-04 12:27:44 -07:00
|
|
|
|
// Check if source has wildcard in attribute portion.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bWild = false;
|
|
|
|
|
|
const UTF8 *p = oldpair;
|
|
|
|
|
|
while (*p && *p != '/')
|
|
|
|
|
|
{
|
|
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (*p == '/')
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-04 12:27:44 -07:00
|
|
|
|
p++;
|
|
|
|
|
|
while (*p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-04 12:27:44 -07:00
|
|
|
|
if (*p == '*' || *p == '?')
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-04 12:27:44 -07:00
|
|
|
|
bWild = true;
|
|
|
|
|
|
break;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-04 12:27:44 -07:00
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bWild)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Wildcard path: iterate all matching attributes on source object.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
olist_push();
|
|
|
|
|
|
if (!parse_attrib_wild(executor, oldpair, &thing, false, false, false))
|
|
|
|
|
|
{
|
|
|
|
|
|
olist_pop();
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2026-03-04 12:27:44 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int atr;
|
|
|
|
|
|
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
|
|
|
|
|
{
|
|
|
|
|
|
ATTR *ap = atr_num(atr);
|
|
|
|
|
|
if (ap)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-04 12:27:44 -07:00
|
|
|
|
attr_info src(thing, ap);
|
|
|
|
|
|
for (int i = 0; i < nargs; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
attr_info dest(executor, newpair[i], true, false);
|
|
|
|
|
|
if (!dest.m_bValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Good_obj(dest.m_object))
|
|
|
|
|
|
{
|
|
|
|
|
|
dest.m_attr = src.m_attr;
|
|
|
|
|
|
dest.m_bValid = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dest.m_bValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
copy_attr(executor, src, dest, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-04 12:27:44 -07:00
|
|
|
|
olist_pop();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-04-25 16:41:26 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-04 12:27:44 -07:00
|
|
|
|
// Single-attribute path.
|
|
|
|
|
|
//
|
|
|
|
|
|
attr_info src(executor, oldpair, false, true);
|
|
|
|
|
|
if (src.m_bValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < nargs; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
attr_info dest(executor, newpair[i], true, false);
|
|
|
|
|
|
if (!dest.m_bValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Good_obj(dest.m_object))
|
|
|
|
|
|
{
|
|
|
|
|
|
dest.m_attr = src.m_attr;
|
|
|
|
|
|
dest.m_bValid = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dest.m_bValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
copy_attr(executor, src, dest, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2026-03-04 12:27:44 -07:00
|
|
|
|
}
|
2007-04-25 16:41:26 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-12-27 17:53:42 +00:00
|
|
|
|
void do_mvattr(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UTF8 *what, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2006-12-27 17:53:42 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Make sure we have something to do.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (nargs < 2)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Nothing to do."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find and make sure we control the target object.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref thing = match_controlled(executor, what);
|
|
|
|
|
|
if (thing == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Look up the source attribute. If it either doesn't exist or isn't
|
|
|
|
|
|
// readable, use an empty string.
|
|
|
|
|
|
//
|
|
|
|
|
|
int in_anum = -1;
|
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 astr = LBuf_Src("do_mvattr");
|
2007-03-14 19:32:44 +00:00
|
|
|
|
ATTR *in_attr = atr_str(args[0]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int aflags = 0;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (in_attr == nullptr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
*astr = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
atr_get_str(astr, thing, in_attr->number, &aowner, &aflags);
|
|
|
|
|
|
if (See_attr(executor, thing, in_attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
in_anum = in_attr->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*astr = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copy the attribute to each target in turn.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bCanDelete = true;
|
|
|
|
|
|
int nCopied = 0;
|
|
|
|
|
|
for (int i = 1; i < nargs; i++)
|
|
|
|
|
|
{
|
2007-03-14 19:32:44 +00:00
|
|
|
|
int anum = mkattr(executor, args[i]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (anum <= 0)
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: That’s not a good name for an attribute."), args[i]));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
ATTR *out_attr = atr_num(anum);
|
|
|
|
|
|
if (!out_attr)
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i]));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (out_attr->number == in_anum)
|
|
|
|
|
|
{
|
|
|
|
|
|
// It doesn't make sense to delete a source attribute if it's also
|
|
|
|
|
|
// included as a destination.
|
|
|
|
|
|
//
|
|
|
|
|
|
bCanDelete = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!bCanSetAttr(executor, thing, out_attr))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i]));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
nCopied++;
|
|
|
|
|
|
atr_add(thing, out_attr->number, astr, Owner(executor), aflags);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Set."), out_attr->name));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Remove the source attribute if we were able to copy it successfully to
|
|
|
|
|
|
// even one destination object.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (nCopied <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (in_attr)
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Not copied anywhere. Not cleared."), in_attr->name));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Not copied anywhere. Non-existent attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if ( in_anum > 0
|
|
|
|
|
|
&& bCanDelete)
|
|
|
|
|
|
{
|
|
|
|
|
|
in_attr = atr_num(in_anum);
|
|
|
|
|
|
if (in_attr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bCanSetAttr(executor, thing, in_attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_clr(thing, in_attr->number);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Cleared."), in_attr->name));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor,
|
2026-07-27 15:04:54 +00:00
|
|
|
|
tprintf(M_("%s: Could not remove old attribute. Permission denied."),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
in_attr->name));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Could not remove old attribute. Non-existent attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * parse_attrib, parse_attrib_wild: parse <obj>/<attr> tokens.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-04-09 05:56:51 +00:00
|
|
|
|
bool parse_attrib(dbref player, const UTF8 *str, dbref *thing, ATTR **attr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
ATTR *tattr = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*thing = NOTHING;
|
|
|
|
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
|
|
{
|
|
|
|
|
|
*attr = tattr;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Break apart string into obj and attr.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
const UTF8 *AttrName;
|
|
|
|
|
|
bool retval = parse_thing_slash(player, str, &AttrName, thing);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Get the named attribute from the object if we can.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (retval)
|
|
|
|
|
|
{
|
2007-03-14 19:32:44 +00:00
|
|
|
|
tattr = atr_str(AttrName);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*attr = tattr;
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-04-09 06:46:29 +00:00
|
|
|
|
void find_wild_attrs(dbref player, dbref thing, const UTF8 *str, bool check_exclude, bool hash_insert, bool get_locks)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int ca, ok, aflags;
|
|
|
|
|
|
|
|
|
|
|
|
// Walk the attribute list of the object.
|
|
|
|
|
|
//
|
|
|
|
|
|
atr_push();
|
2007-03-14 06:34:45 +00:00
|
|
|
|
unsigned char *as;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as))
|
|
|
|
|
|
{
|
|
|
|
|
|
pattr = atr_num(ca);
|
|
|
|
|
|
|
|
|
|
|
|
// Discard bad attributes and ones we've seen before.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!pattr)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( check_exclude
|
|
|
|
|
|
&& ( (pattr->flags & AF_PRIVATE)
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
|| mudstate.parent_htab.find(ca) != mudstate.parent_htab.end()))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we aren't the top level remember this attr so we exclude it in
|
|
|
|
|
|
// any parents.
|
|
|
|
|
|
//
|
|
|
|
|
|
atr_get_info(thing, ca, &aowner, &aflags);
|
|
|
|
|
|
if ( check_exclude
|
|
|
|
|
|
&& (aflags & AF_PRIVATE))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (get_locks)
|
|
|
|
|
|
{
|
|
|
|
|
|
ok = bCanReadAttr(player, thing, pattr, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ok = See_attr(player, thing, pattr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mudstate.wild_invk_ctr = 0;
|
|
|
|
|
|
if ( ok
|
2007-03-14 00:55:08 +00:00
|
|
|
|
&& quick_wild(str, pattr->name))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
olist_add(ca);
|
|
|
|
|
|
if (hash_insert)
|
|
|
|
|
|
{
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.parent_htab.emplace(ca, pattr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
atr_pop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-04-09 05:56:51 +00:00
|
|
|
|
bool parse_attrib_wild(dbref player, const UTF8 *str, dbref *thing,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool check_parents, bool get_locks, bool df_star)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!str)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref parent;
|
|
|
|
|
|
int lev;
|
|
|
|
|
|
bool check_exclude, hash_insert;
|
2007-04-09 05:56:51 +00:00
|
|
|
|
const UTF8 *after = str;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Separate name and attr portions at the first /.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
if (!parse_thing_slash(player, str, &after, thing))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Not in obj/attr format, return if not defaulting to.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!df_star)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Look for the object, return failure if not found.
|
|
|
|
|
|
//
|
2007-04-09 05:56:51 +00:00
|
|
|
|
init_match(player, str, NOTYPE);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
match_everything(MAT_EXIT_PARENTS);
|
|
|
|
|
|
*thing = match_result();
|
|
|
|
|
|
|
|
|
|
|
|
if (!Good_obj(*thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2007-04-09 05:56:51 +00:00
|
|
|
|
after = T("*");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check the object (and optionally all parents) for attributes.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (check_parents)
|
|
|
|
|
|
{
|
|
|
|
|
|
check_exclude = false;
|
|
|
|
|
|
hash_insert = check_parents;
|
Replace 8 CHashTable instances with std::unordered_map (Phase 1).
Migrate command_htab, logout_cmd_htab, player_htab, powers_htab,
reference_htab, ufunc_htab to StringPtrMap and mail_htab, parent_htab,
pcache_htab to DbrefPtrMap. Eliminate the htab wrapper layer
(hashfindLEN, hashaddLEN, hashdeleteLEN, hashreplLEN, hashreplall,
hashflush, hashreset, hash_firstentry/nextentry/firstkey/nextkey)
and convert all 132 call sites to direct STL map operations.
Help system tables (HELP_DESC.ht) also migrated. vattr_name_htab
remains CHashTable for Phase 2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:37 -07:00
|
|
|
|
mudstate.parent_htab.clear();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ITER_PARENTS(*thing, parent, lev)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Good_obj(Parent(parent)))
|
|
|
|
|
|
{
|
|
|
|
|
|
hash_insert = false;
|
|
|
|
|
|
}
|
2007-04-09 05:56:51 +00:00
|
|
|
|
find_wild_attrs(player, parent, after, check_exclude, hash_insert, get_locks);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
check_exclude = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2007-04-09 05:56:51 +00:00
|
|
|
|
find_wild_attrs(player, *thing, after, false, false, get_locks);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * edit_string, edit_string_ansi, do_edit: Modify attributes.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
void edit_string(UTF8 *src, UTF8 **dst, UTF8 *from, UTF8 *to)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *cp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM.
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
if (!strcmp(reinterpret_cast<char *>(from), "^"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Prepend 'to' to string.
|
|
|
|
|
|
//
|
|
|
|
|
|
*dst = alloc_lbuf("edit_string.^");
|
|
|
|
|
|
cp = *dst;
|
|
|
|
|
|
safe_str(to, *dst, &cp);
|
|
|
|
|
|
safe_str(src, *dst, &cp);
|
|
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(from), "$"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Append 'to' to string.
|
|
|
|
|
|
//
|
|
|
|
|
|
*dst = alloc_lbuf("edit_string.$");
|
|
|
|
|
|
cp = *dst;
|
|
|
|
|
|
safe_str(src, *dst, &cp);
|
|
|
|
|
|
safe_str(to, *dst, &cp);
|
|
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Replace all occurances of 'from' with 'to'. Handle the special
|
|
|
|
|
|
// cases of from = \$ and \^.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( ( from[0] == '\\'
|
|
|
|
|
|
|| from[0] == '%')
|
|
|
|
|
|
&& ( from[1] == '$'
|
|
|
|
|
|
|| from[1] == '^')
|
|
|
|
|
|
&& from[2] == '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
from++;
|
|
|
|
|
|
}
|
|
|
|
|
|
*dst = replace_string(from, to, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
static void edit_string_ansi(UTF8 *src, UTF8 **dst, UTF8 **returnstr, UTF8 *from, UTF8 *to)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *cp, *rp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
if (!strcmp(reinterpret_cast<char *>(from), "^"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Prepend 'to' to string.
|
|
|
|
|
|
//
|
|
|
|
|
|
*dst = alloc_lbuf("edit_string.^");
|
|
|
|
|
|
cp = *dst;
|
|
|
|
|
|
safe_str(to, *dst, &cp);
|
|
|
|
|
|
safe_str(src, *dst, &cp);
|
|
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
// Do the ansi string used to notify.
|
|
|
|
|
|
//
|
|
|
|
|
|
*returnstr = alloc_lbuf("edit_string_ansi.^");
|
|
|
|
|
|
rp = *returnstr;
|
2026-03-03 19:38:14 -07:00
|
|
|
|
safe_str(reinterpret_cast<const UTF8 *>(COLOR_INTENSE), *returnstr, &rp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_str(to, *returnstr, &rp);
|
2026-03-03 19:38:14 -07:00
|
|
|
|
safe_str(reinterpret_cast<const UTF8 *>(COLOR_RESET), *returnstr, &rp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_str(src, *returnstr, &rp);
|
|
|
|
|
|
*rp = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-03-03 18:39:35 -07:00
|
|
|
|
else if (!strcmp(reinterpret_cast<char *>(from), "$"))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Append 'to' to string
|
|
|
|
|
|
//
|
|
|
|
|
|
*dst = alloc_lbuf("edit_string.$");
|
|
|
|
|
|
cp = *dst;
|
|
|
|
|
|
safe_str(src, *dst, &cp);
|
|
|
|
|
|
safe_str(to, *dst, &cp);
|
|
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
// Do the ansi string used to notify.
|
|
|
|
|
|
//
|
|
|
|
|
|
*returnstr = alloc_lbuf("edit_string_ansi.$");
|
|
|
|
|
|
rp = *returnstr;
|
|
|
|
|
|
safe_str(src, *returnstr, &rp);
|
2026-03-03 19:38:14 -07:00
|
|
|
|
safe_str(reinterpret_cast<const UTF8 *>(COLOR_INTENSE), *returnstr, &rp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_str(to, *returnstr, &rp);
|
2026-03-03 19:38:14 -07:00
|
|
|
|
safe_str(reinterpret_cast<const UTF8 *>(COLOR_RESET), *returnstr, &rp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
*rp = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Replace all occurances of 'from' with 'to'. Handle the special
|
|
|
|
|
|
// cases of from = \$ and \^.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( ((from[0] == '\\') || (from[0] == '%'))
|
|
|
|
|
|
&& ((from[1] == '$') || (from[1] == '^'))
|
|
|
|
|
|
&& ( from[2] == '\0'))
|
|
|
|
|
|
{
|
|
|
|
|
|
from++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*dst = replace_string(from, to, src);
|
2008-12-27 18:37:01 -08:00
|
|
|
|
*returnstr = replace_string(from, tprintf(T("%s%s%s"), COLOR_INTENSE,
|
2007-03-13 06:33:05 +00:00
|
|
|
|
to, COLOR_RESET), src);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-12-27 17:53:42 +00:00
|
|
|
|
void do_edit(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UTF8 *it, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2006-12-27 17:53:42 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
|
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing, aowner;
|
|
|
|
|
|
int atr, aflags;
|
|
|
|
|
|
bool bGotOne;
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *from, *to, *result, *returnstr, *atext;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure we have something to do.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( nargs < 1
|
|
|
|
|
|
|| !*args[0])
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Nothing to do."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
from = args[0];
|
2026-03-03 18:39:35 -07:00
|
|
|
|
to = (nargs >= 2) ? args[1] : const_cast<UTF8 *>(T(""));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
// Look for the object and get the attribute (possibly wildcarded)
|
|
|
|
|
|
//
|
|
|
|
|
|
olist_push();
|
|
|
|
|
|
if ( !it
|
|
|
|
|
|
|| !*it
|
|
|
|
|
|
|| !parse_attrib_wild(executor, it, &thing, false, false, false))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Iterate through matching attributes, performing edit.
|
|
|
|
|
|
//
|
|
|
|
|
|
bGotOne = 0;
|
|
|
|
|
|
atext = alloc_lbuf("do_edit.atext");
|
|
|
|
|
|
bool could_hear = Hearer(thing);
|
|
|
|
|
|
|
|
|
|
|
|
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
|
|
|
|
|
{
|
|
|
|
|
|
ap = atr_num(atr);
|
|
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get the attr and make sure we can modify it.
|
|
|
|
|
|
//
|
|
|
|
|
|
atr_get_str(atext, thing, ap->number, &aowner, &aflags);
|
|
|
|
|
|
if (bCanSetAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Do the edit and save the result
|
|
|
|
|
|
//
|
|
|
|
|
|
bGotOne = true;
|
|
|
|
|
|
edit_string_ansi(atext, &result, &returnstr, from, to);
|
|
|
|
|
|
atr_add(thing, ap->number, result, Owner(executor), aflags);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("Set - %s: %s"), ap->name,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
returnstr));
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(result);
|
|
|
|
|
|
free_lbuf(returnstr);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// No rights to change the attr.
|
|
|
|
|
|
//
|
2020-06-28 18:47:38 -06:00
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 15:04:54 +00:00
|
|
|
|
notify_quiet(executor, tprintf(M_("%s: Permission denied."), ap->name));
|
2020-06-28 18:47:38 -06:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up.
|
|
|
|
|
|
//
|
|
|
|
|
|
free_lbuf(atext);
|
|
|
|
|
|
olist_pop();
|
|
|
|
|
|
|
|
|
|
|
|
if (!bGotOne)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No matching attributes."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
handle_ears(thing, could_hear, Hearer(thing));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_wipe(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *it, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
|
|
|
|
|
|
olist_push();
|
|
|
|
|
|
if ( !it
|
|
|
|
|
|
|| !*it
|
|
|
|
|
|
|| !parse_attrib_wild(executor, it, &thing, false, false, true))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( mudconf.safe_wipe
|
2007-03-14 22:33:04 +00:00
|
|
|
|
&& has_flag(NOTHING, thing, T("SAFE")))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("SAFE objects may not be @wiped."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Iterate through matching attributes, zapping the writable ones
|
|
|
|
|
|
//
|
|
|
|
|
|
int atr;
|
|
|
|
|
|
ATTR *ap;
|
|
|
|
|
|
bool bGotOne = false, could_hear = Hearer(thing);
|
|
|
|
|
|
|
|
|
|
|
|
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
|
|
|
|
|
{
|
|
|
|
|
|
ap = atr_num(atr);
|
|
|
|
|
|
if (ap)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get the attr and make sure we can modify it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (bCanSetAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
atr_clr(thing, ap->number);
|
|
|
|
|
|
bGotOne = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up
|
|
|
|
|
|
//
|
|
|
|
|
|
olist_pop();
|
|
|
|
|
|
|
|
|
|
|
|
if (!bGotOne)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No matching attributes."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
set_modified(thing);
|
|
|
|
|
|
handle_ears(thing, could_hear, Hearer(thing));
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Wiped."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-12-27 17:53:42 +00:00
|
|
|
|
void do_trigger(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-09-02 22:02:33 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
|
|
|
|
|
|
if (!( parse_attrib(executor, object, &thing, &pattr)
|
|
|
|
|
|
&& pattr))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-07 00:08:14 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!Controls(executor, thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-04 14:46:56 -07:00
|
|
|
|
|
|
|
|
|
|
if (key & TRIG_NOW)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Execute the attribute inline instead of enqueuing.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
UTF8 *act = atr_pget(thing, pattr->number, &aowner, &aflags);
|
|
|
|
|
|
if (*act)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool bRun = true;
|
|
|
|
|
|
dbref aowner2;
|
|
|
|
|
|
int aflags2;
|
|
|
|
|
|
UTF8 *charges = atr_pget(thing, A_CHARGES, &aowner2, &aflags2);
|
|
|
|
|
|
if (*charges)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// int64_t, not int (#1402): large charges must not wrap
|
|
|
|
|
|
// to a small positive and skip RUNOUT.
|
|
|
|
|
|
//
|
|
|
|
|
|
int64_t num = mux_atoi64(charges);
|
2026-03-04 14:46:56 -07:00
|
|
|
|
if (num > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
UTF8 *buff = alloc_sbuf("trigger.now.charges");
|
2026-07-28 19:34:54 -06:00
|
|
|
|
mux_i64toa(num - 1, buff);
|
2026-03-04 14:46:56 -07:00
|
|
|
|
atr_add_raw(thing, A_CHARGES, buff);
|
|
|
|
|
|
free_sbuf(buff);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
UTF8 *buff = atr_pget(thing, A_RUNOUT, &aowner2, &aflags2);
|
|
|
|
|
|
if (*buff)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(act);
|
|
|
|
|
|
act = buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(buff);
|
|
|
|
|
|
bRun = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(charges);
|
|
|
|
|
|
if (bRun)
|
|
|
|
|
|
{
|
Implement NOEVAL object flag and attribute flag (#11).
Add NOEVAL object flag (FLAG_WORD3) and AF_NOEVAL attribute flag so that
attribute contents can be stored as literal data without evaluation.
When either flag is set, u(), ulocal(), get_eval(), @trigger, did_it()
messages/actions, @function, modSpeech, check_filter, make_prefix,
process_hook, sortby comparison, eval_boolexp, and format attributes
(EXITFORMAT/CONFORMAT/NAMEFORMAT/DESCFORMAT) all return raw text with
no function calls, %-substitutions, or escape processing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:04:14 -07:00
|
|
|
|
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// NOEVAL content is data, not code -- skip execution.
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
process_command(thing, executor, executor,
|
|
|
|
|
|
AttrTrace(aflags, 0), false, act,
|
|
|
|
|
|
(const UTF8 **)argv, nargs);
|
|
|
|
|
|
}
|
2026-03-04 14:46:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(act);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
did_it(executor, thing, 0, nullptr, 0, nullptr, pattr->number, 0, (const UTF8 **)argv, nargs);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2006-09-07 00:08:14 +00:00
|
|
|
|
if (key & TRIG_NOTIFY)
|
|
|
|
|
|
{
|
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 tbuf = LBuf_Src("trigger.notify_cmd");
|
2007-03-14 22:33:04 +00:00
|
|
|
|
mux_strncpy(tbuf, T("@notify/quiet me"), LBUF_SIZE-1);
|
2006-09-07 00:08:14 +00:00
|
|
|
|
CLinearTimeAbsolute lta;
|
2006-12-27 17:53:42 +00:00
|
|
|
|
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, A_SEMAPHORE,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
tbuf,
|
2018-10-03 17:54:51 +00:00
|
|
|
|
0, nullptr,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
mudstate.global_regs);
|
2006-09-07 00:08:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if ( !(key & TRIG_QUIET)
|
|
|
|
|
|
&& !Quiet(executor))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("Triggered."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_include: Inline-expand the contents of an attribute into the current
|
|
|
|
|
|
// action list. Unlike @trigger, this does not create a new queue entry --
|
|
|
|
|
|
// the included commands execute immediately in the caller's context, and
|
|
|
|
|
|
// @break/@assert propagate to the parent action list (unless /nobreak).
|
|
|
|
|
|
//
|
|
|
|
|
|
// Syntax: @include[/nobreak][/localize][/clearregs] obj/attr[=arg0,arg1,...]
|
|
|
|
|
|
//
|
|
|
|
|
|
// Without arguments, the parent's %0-%9 pass through unchanged.
|
|
|
|
|
|
// With arguments, %0-%9 are replaced for the duration of the include.
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_include(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
|
|
|
|
|
UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
|
|
|
|
|
|
if (!( parse_attrib(executor, object, &thing, &pattr)
|
|
|
|
|
|
&& pattr))
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-25 19:47:10 -06:00
|
|
|
|
// @include requires read access, not control: the attribute text is
|
|
|
|
|
|
// expanded into the *includer's* action list (executor stays the caller).
|
|
|
|
|
|
// Running as the source object with only See_attr would let a player
|
|
|
|
|
|
// execute with foreign privileges by including any VISUAL attr (#1279).
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
//
|
|
|
|
|
|
if (!See_attr(executor, thing, pattr))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
int aflags;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf act = LBuf_Adopt(atr_pget(thing, pattr->number, &aowner, &aflags));
|
|
|
|
|
|
if (!*act.get())
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
// NOEVAL content is data, not code -- skip execution.
|
|
|
|
|
|
//
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Strip $command: or ^listen: prefix if present, so that the same
|
|
|
|
|
|
// attribute that serves as a $-command can be @included as a
|
|
|
|
|
|
// subroutine.
|
|
|
|
|
|
//
|
|
|
|
|
|
UTF8 *body = act;
|
|
|
|
|
|
if ( '$' == act[0]
|
|
|
|
|
|
|| '^' == act[0])
|
|
|
|
|
|
{
|
2026-04-05 18:34:51 -06:00
|
|
|
|
UTF8 *colon = (UTF8 *)strchr((char *)act.get(), ':');
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
if (colon)
|
|
|
|
|
|
{
|
|
|
|
|
|
body = colon + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Determine effective environment: if caller supplies args, use them;
|
|
|
|
|
|
// otherwise pass through the parent's environment.
|
|
|
|
|
|
//
|
|
|
|
|
|
const UTF8 **env;
|
|
|
|
|
|
int nenv;
|
|
|
|
|
|
if ( nargs > 0
|
|
|
|
|
|
&& argv[0]
|
|
|
|
|
|
&& argv[0][0] != '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
env = (const UTF8 **)argv;
|
|
|
|
|
|
nenv = nargs;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
env = cargs;
|
|
|
|
|
|
nenv = ncargs;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Save/clear %q-registers if requested.
|
|
|
|
|
|
//
|
|
|
|
|
|
reg_ref *preserve[MAX_GLOBAL_REGS];
|
|
|
|
|
|
bool bLocalize = (key & INCLUDE_LOCALIZE) != 0;
|
|
|
|
|
|
if (bLocalize)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
preserve[i] = mudstate.global_regs[i];
|
|
|
|
|
|
if (preserve[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
RegAddRef(preserve[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (key & INCLUDE_CLEARREGS)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mudstate.global_regs[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(mudstate.global_regs[i]);
|
|
|
|
|
|
mudstate.global_regs[i] = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Save break state so /nobreak can isolate it.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool save_break = break_called;
|
|
|
|
|
|
if (key & INCLUDE_NOBREAK)
|
|
|
|
|
|
{
|
|
|
|
|
|
break_called = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Walk the included text, splitting on semicolons and executing
|
2026-07-25 19:47:10 -06:00
|
|
|
|
// each command inline as the includer (not the source object —
|
|
|
|
|
|
// #1279), with ;| piping honored like the queued runner (#788).
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
//
|
2026-07-25 19:47:10 -06:00
|
|
|
|
process_command_list_inline(executor, caller, enactor,
|
2026-06-10 09:00:21 -06:00
|
|
|
|
AttrTrace(aflags, 0), body, env, nenv);
|
Add PennMUSH and RhostMUSH feature adoption
PennMUSH: #lambda/#apply anonymous inline attributes, @include with
/nobreak /localize /clearregs, channel mogrifiers (MOGRIFY`BLOCK,
MESSAGE, FORMAT, OVERRIDE, NOBUFFER), cmogrifier().
RhostMUSH: between(), delextract(), garble(), caplist(), moon(),
soundex(), soundlike(), while(), crc32obj(), subnetmatch(),
wrapcolumns(), sandbox() with AST fallback for JIT compatibility.
21 new features/functions, 15 new smoke test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:08 -06:00
|
|
|
|
|
|
|
|
|
|
// Restore break state for /nobreak.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (key & INCLUDE_NOBREAK)
|
|
|
|
|
|
{
|
|
|
|
|
|
break_called = save_break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Restore %q-registers if /localize.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (bLocalize)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mudstate.global_regs[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(mudstate.global_regs[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
mudstate.global_regs[i] = preserve[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-02 13:48:10 -07:00
|
|
|
|
void do_use(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *object, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref thing, aowner;
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
|
|
|
|
|
|
init_match(executor, object, NOTYPE);
|
|
|
|
|
|
match_neighbor();
|
|
|
|
|
|
match_possession();
|
|
|
|
|
|
if (Wizard(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
match_absolute();
|
|
|
|
|
|
match_player();
|
|
|
|
|
|
}
|
|
|
|
|
|
match_me();
|
|
|
|
|
|
match_here();
|
|
|
|
|
|
thing = noisy_match_result();
|
|
|
|
|
|
if (thing == NOTHING)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure player can use it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!could_doit(executor, thing, A_LUSE))
|
|
|
|
|
|
{
|
|
|
|
|
|
did_it(executor, thing, A_UFAIL,
|
2026-07-27 02:31:36 +00:00
|
|
|
|
M_("You can’t figure out how to use that."),
|
2018-10-03 17:54:51 +00:00
|
|
|
|
A_OUFAIL, nullptr, A_AUFAIL, 0, nullptr, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
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 temp = LBuf_Src("do_use");
|
2006-09-01 22:59:23 +00:00
|
|
|
|
bool doit = false;
|
|
|
|
|
|
if (*atr_pget_str(temp, thing, A_USE, &aowner, &aflags))
|
|
|
|
|
|
{
|
|
|
|
|
|
doit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (*atr_pget_str(temp, thing, A_OUSE, &aowner, &aflags))
|
|
|
|
|
|
{
|
|
|
|
|
|
doit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (*atr_pget_str(temp, thing, A_AUSE, &aowner, &aflags))
|
|
|
|
|
|
{
|
|
|
|
|
|
doit = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (doit)
|
|
|
|
|
|
{
|
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 df_use = LBuf_Src("do_use.use");
|
|
|
|
|
|
LBuf df_ouse = LBuf_Src("do_use.ouse");
|
2026-07-27 15:04:54 +00:00
|
|
|
|
mux_sprintf(df_use, LBUF_SIZE, M_("You use %s"), Moniker(thing));
|
|
|
|
|
|
mux_sprintf(df_ouse, LBUF_SIZE, M_("uses %s"), Moniker(thing));
|
2006-11-27 05:25:42 +00:00
|
|
|
|
did_it(executor, thing, A_USE, df_use, A_OUSE, df_ouse, A_AUSE, 0,
|
2018-10-03 17:54:51 +00:00
|
|
|
|
nullptr, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("You can’t figure out how to use that."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
|
|
* * do_setvattr: Set a user-named (or possibly a predefined) attribute.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void do_setvattr
|
|
|
|
|
|
(
|
|
|
|
|
|
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
|
|
|
|
)
|
|
|
|
|
|
{
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(key);
|
|
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *s;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int anum;
|
|
|
|
|
|
|
|
|
|
|
|
// Skip the '&'
|
|
|
|
|
|
//
|
|
|
|
|
|
arg1++;
|
|
|
|
|
|
|
|
|
|
|
|
// Take to the space
|
|
|
|
|
|
//
|
|
|
|
|
|
for (s = arg1; *s && !mux_isspace(*s); s++)
|
|
|
|
|
|
{
|
|
|
|
|
|
; // Nothing.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Split it
|
|
|
|
|
|
//
|
|
|
|
|
|
if (*s)
|
|
|
|
|
|
{
|
|
|
|
|
|
*s++ = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get or make attribute
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
anum = mkattr(executor, reinterpret_cast<UTF8 *>(arg1));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (anum <= 0)
|
|
|
|
|
|
{
|
2026-07-27 02:31:36 +00:00
|
|
|
|
notify_quiet(executor, M_("That’s not a good name for an attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-03 17:54:51 +00:00
|
|
|
|
do_setattr(executor, caller, enactor, 0, anum, 2, s, arg2, nullptr, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|