mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Found while translating mail (#1419). The tell was a line wrong in English before translation is even involved: MAIL: 1 messages in folder 0 [unnamed] (1 unread, 0 cleared). MN_() exists for this (#1622) and #1655 converted the @mail/stats family; this set was missed. A translator cannot work around it -- the msgid has no msgid_plural, so there is no second form for a catalogue to supply, and es must pick one wording for all counts while ru and pl need three and can express none of them. The fix is in the C++, not the .po. ## Converted, 11 sites mail.cpp, mail_mod.cpp mailbox is full; URGENT MAIL urgent messages walkdb.cpp universe contains; objects @chowned; objects marked object.cpp objects @chowned to you wiz.cpp You toaded ... objects @chowned command.cpp at most N players logged in The mail module duplicates are converted alongside the engine's so the two implementations do not drift (#1614). ## One was a different bug than the issue described walkdb.cpp built its message as M_("%d objects %smarked") with %s = "" or "un". That is fragment assembly, not a missing plural: a translator handed that %s cannot use it, because the prefix is English morphology rather than a word. Split into two whole sentences, each pluralised -- the #1575 / #1588 rule. Now renders "1 object marked" / "8 objects unmarked" instead of a %s a catalogue has no way to fill. ## Two deliberately NOT converted MAIL: %d messages in folder %d [%s] (%d unread, %d cleared). %d objects = %d rooms, %d exits, %d things, %d players. (%d garbage) MN_(s, p, n) selects one form from one n. Four independent counts are four independent plural decisions, and in Russian each noun needs its own form. These want rewording, which is a design call rather than a mechanical conversion, so they are left and recorded rather than half-fixed. ## Catalogue fallout .pot 18 -> 27 plural entries; all three catalogues msgmerge'd. xx is `complete` policy, so its nine new entries had to be filled and unfuzzied or the guard fails -- which it duly did, first try. msgmerge had copied the old singular-only text into BOTH forms, so msgstr[0] said "messages"; each form is now prefixed from its own msgid. es and ko received real translations for all nine. es.po 190 -> 198/976 (20.3%) ko.po 229 -> 236/976 (24.2%) ## Verified end to end, at n=1 and n>1 en 1 object marked 8 objects marked es 1 objeto marcado 8 objetos marcados ko 사물 1개를 표시했습니다 사물 8개를 표시했습니다 xx [xx] 1 object marked [xx] 8 objects marked Korean renders identically for both, which is correct: it declares nplurals=1, so the catalogue's own rule is being honoured rather than English's imposed on it. The unconverted multi-count line still reads "1 messages", as documented. make test: Smoke 1561 x3, tests/nls 7, ko 4, plural 43. TESTEXIT=0. Refs #1419, #1575, #1588, #1614, #1622, #1655, #1702. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
912 lines
24 KiB
C++
912 lines
24 KiB
C++
/*! \file wiz.cpp
|
||
* \brief Wizard-only commands.
|
||
*
|
||
*/
|
||
|
||
#include "copyright.h"
|
||
#include "autoconf.h"
|
||
#include "config.h"
|
||
#include "externs.h"
|
||
#include "alloc.h"
|
||
|
||
static void do_teleport_single
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int key,
|
||
dbref victim,
|
||
UTF8 *to
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
|
||
dbref loc;
|
||
int hush = 0;
|
||
|
||
// Validate type of victim.
|
||
//
|
||
if ( !Good_obj(victim)
|
||
|| isRoom(victim))
|
||
{
|
||
notify_quiet(executor, M_("You can’t teleport that."));
|
||
return;
|
||
}
|
||
|
||
// Fail if we don't control the victim or the victim's location.
|
||
//
|
||
if ( !Controls(executor, victim)
|
||
&& !Controls(executor, isExit(victim) ? Exits(victim) : Location(victim))
|
||
&& !Tel_Anything(executor))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
// Check for teleporting home
|
||
//
|
||
if (!string_compare(to, T("home")))
|
||
{
|
||
if (isExit(victim))
|
||
{
|
||
notify_quiet(executor, M_("Bad destination."));
|
||
}
|
||
else
|
||
{
|
||
move_via_teleport(victim, HOME, enactor, 0);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Find out where to send the victim.
|
||
//
|
||
init_match(executor, to, NOTYPE);
|
||
match_everything(0);
|
||
dbref destination = match_result();
|
||
|
||
switch (destination)
|
||
{
|
||
case NOTHING:
|
||
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
|
||
case AMBIGUOUS:
|
||
|
||
notify_quiet(executor, M_("I don’t know which destination you mean!"));
|
||
return;
|
||
|
||
default:
|
||
|
||
if (victim == destination)
|
||
{
|
||
notify_quiet(executor, M_("Bad destination."));
|
||
return;
|
||
}
|
||
}
|
||
|
||
// If fascist teleport is on, you must control the victim's ultimate
|
||
// location (after LEAVEing any objects) or it must be JUMP_OK.
|
||
//
|
||
if (mudconf.fascist_tport)
|
||
{
|
||
if (isExit(victim))
|
||
{
|
||
loc = where_room(Home(victim));
|
||
}
|
||
else
|
||
{
|
||
loc = where_room(victim);
|
||
}
|
||
|
||
if ( !Good_obj(loc)
|
||
|| !isRoom(loc)
|
||
|| !( Controls(executor, loc)
|
||
|| Jump_ok(loc)
|
||
|| Tel_Anywhere(executor)))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ( isGarbage(destination)
|
||
|| ( Has_location(destination)
|
||
&& isGarbage(Location(destination))))
|
||
{
|
||
// @Teleporting into garbage is never permitted.
|
||
//
|
||
notify_quiet(executor, M_("Bad destination."));
|
||
return;
|
||
}
|
||
else if (Has_contents(destination))
|
||
{
|
||
// You must control the destination OR it must be a JUMP_OK where
|
||
// the victim passes its TELEPORT lock (exit victims have the
|
||
// additional requirement that the destination must be OPEN_OK and
|
||
// the victim must pass the destination's OPEN lock) OR you must be
|
||
// Tel_Anywhere.
|
||
//
|
||
// Only God may teleport exits into God.
|
||
//
|
||
if ( ( Controls(executor, destination)
|
||
|| Tel_Anywhere(executor)
|
||
|| ( Jump_ok(destination)
|
||
&& could_doit(victim, destination, A_LTPORT)
|
||
&& ( !isExit(victim)
|
||
|| ( Open_ok(destination)
|
||
&& could_doit(executor, destination, A_LOPEN)))))
|
||
&& ( !isExit(victim)
|
||
|| !God(destination)
|
||
|| God(executor)))
|
||
{
|
||
// We're OK, do the teleport.
|
||
//
|
||
if (key & TELEPORT_QUIET)
|
||
{
|
||
hush = HUSH_ENTER | HUSH_LEAVE;
|
||
}
|
||
|
||
if (move_via_teleport(victim, destination, enactor, hush))
|
||
{
|
||
if (executor != victim)
|
||
{
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Teleported."));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Nope, report failure.
|
||
//
|
||
if (executor != victim)
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
did_it(victim, destination,
|
||
A_TFAIL, T("You can’t teleport there!"),
|
||
A_OTFAIL, 0, A_ATFAIL, 0, nullptr, 0);
|
||
}
|
||
}
|
||
else if (isExit(destination))
|
||
{
|
||
if (isExit(victim))
|
||
{
|
||
if (executor != victim)
|
||
{
|
||
notify_quiet(executor, M_("Bad destination."));
|
||
}
|
||
did_it(victim, destination,
|
||
A_TFAIL, T("You can’t teleport there!"),
|
||
A_OTFAIL, 0, A_ATFAIL, 0, nullptr, 0);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
if (Exits(destination) == Location(victim))
|
||
{
|
||
move_exit(victim, destination, false, T("You can’t go that way."), 0);
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("I can’t find that exit."));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void do_teleport
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *arg1,
|
||
UTF8 *arg2,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if ( ( Fixed(executor)
|
||
|| Fixed(Owner(executor)))
|
||
&& !Tel_Anywhere(executor))
|
||
{
|
||
notify(executor, mudconf.fixed_tel_msg);
|
||
return;
|
||
}
|
||
|
||
// Get victim.
|
||
//
|
||
if (nargs == 1)
|
||
{
|
||
// Teleport executor to given destination.
|
||
//
|
||
do_teleport_single(executor, caller, enactor, key, executor, arg1);
|
||
}
|
||
else if (nargs == 2)
|
||
{
|
||
// Teleport 3rd part(y/ies) to given destination.
|
||
//
|
||
if (key & TELEPORT_LIST)
|
||
{
|
||
// We have a space-delimited list of victims.
|
||
//
|
||
UTF8 *p;
|
||
string_token st(arg1, T(" "));
|
||
for (p = st.parse(); p; p = st.parse())
|
||
{
|
||
init_match(executor, p, NOTYPE);
|
||
match_everything(0);
|
||
dbref victim = noisy_match_result();
|
||
|
||
if (Good_obj(victim))
|
||
{
|
||
do_teleport_single(executor, caller, enactor, key, victim,
|
||
arg2);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
init_match(executor, arg1, NOTYPE);
|
||
match_everything(0);
|
||
dbref victim = noisy_match_result();
|
||
|
||
if (Good_obj(victim))
|
||
{
|
||
do_teleport_single(executor, caller, enactor, key, victim,
|
||
arg2);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_force_prefixed: Interlude to do_force for the # command
|
||
//
|
||
void do_force_prefixed
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
UTF8 *command,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UTF8 *cp = parse_to(&command, ' ', 0);
|
||
if (!command)
|
||
{
|
||
return;
|
||
}
|
||
while (mux_isspace(*command))
|
||
{
|
||
command++;
|
||
}
|
||
if (*command)
|
||
{
|
||
do_force(executor, caller, enactor, eval, key, 2, cp, command, cargs, ncargs);
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_force: Force an object to do something.
|
||
//
|
||
void do_force
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *arg1,
|
||
UTF8 *arg2,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(nargs);
|
||
|
||
dbref victim = match_controlled(executor, arg1);
|
||
if (victim != NOTHING)
|
||
{
|
||
// Force victim to do command.
|
||
//
|
||
if (key & FORCE_NOW)
|
||
{
|
||
process_command(victim, caller, executor, eval, false, arg2, cargs, ncargs);
|
||
}
|
||
else
|
||
{
|
||
CLinearTimeAbsolute lta;
|
||
wait_que(victim, caller, executor, eval, false, lta, NOTHING, 0,
|
||
arg2,
|
||
ncargs, cargs,
|
||
mudstate.global_regs);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_toad: Turn a player into an object.
|
||
//
|
||
void do_toad
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *toad,
|
||
UTF8 *newowner,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref victim, recipient, loc, aowner;
|
||
UTF8 *buf;
|
||
int count, aflags;
|
||
|
||
init_match(executor, toad, TYPE_PLAYER);
|
||
match_neighbor();
|
||
match_absolute();
|
||
match_player();
|
||
if (!Good_obj(victim = noisy_match_result()))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!isPlayer(victim))
|
||
{
|
||
notify_quiet(executor, M_("Try @destroy instead."));
|
||
return;
|
||
}
|
||
if (No_Destroy(victim))
|
||
{
|
||
notify_quiet(executor, M_("You can’t toad that player."));
|
||
return;
|
||
}
|
||
if ( nargs == 2
|
||
&& *newowner )
|
||
{
|
||
init_match(executor, newowner, TYPE_PLAYER);
|
||
match_neighbor();
|
||
match_absolute();
|
||
match_player();
|
||
if ((recipient = noisy_match_result()) == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (mudconf.toad_recipient == NOTHING)
|
||
{
|
||
recipient = executor;
|
||
}
|
||
else
|
||
{
|
||
recipient = mudconf.toad_recipient;
|
||
}
|
||
}
|
||
|
||
STARTLOG(LOG_WIZARD, "WIZ", "TOAD");
|
||
log_name_and_loc(victim);
|
||
log_text(T(" was @toaded by "));
|
||
log_name(executor);
|
||
ENDLOG;
|
||
|
||
// Clear everything out.
|
||
//
|
||
if (key & TOAD_NO_CHOWN)
|
||
{
|
||
count = -1;
|
||
}
|
||
else
|
||
{
|
||
// You get it.
|
||
//
|
||
count = chown_all(victim, recipient, executor, CHOWN_NOZONE);
|
||
s_Owner(victim, recipient);
|
||
s_Zone(victim, NOTHING);
|
||
}
|
||
s_Flags(victim, FLAG_WORD1, TYPE_THING | HALT);
|
||
s_Flags(victim, FLAG_WORD2, 0);
|
||
s_Flags(victim, FLAG_WORD3, 0);
|
||
s_Pennies(victim, 1);
|
||
|
||
// Notify people.
|
||
//
|
||
loc = Location(victim);
|
||
buf = alloc_mbuf("do_toad");
|
||
const UTF8 *pVictimMoniker = Moniker(victim);
|
||
const UTF8 *pVictimName = Name(victim);
|
||
mux_sprintf(buf, MBUF_SIZE, M_("%s has been turned into a slimy toad!"),
|
||
pVictimMoniker);
|
||
notify_except2(loc, executor, victim, executor, buf);
|
||
mux_sprintf(buf, MBUF_SIZE, MN_("You toaded %s! (%d object @chowned)",
|
||
"You toaded %s! (%d objects @chowned)",
|
||
count + 1),
|
||
pVictimMoniker, count + 1);
|
||
notify_quiet(executor, buf);
|
||
|
||
// Zap the name from the name hash table.
|
||
//
|
||
mux_sprintf(buf, MBUF_SIZE, M_("a slimy toad named %s"), pVictimMoniker);
|
||
delete_player_name(victim, pVictimName, false);
|
||
s_Name(victim, buf);
|
||
free_mbuf(buf);
|
||
|
||
// Zap the alias, too.
|
||
//
|
||
{
|
||
LBuf alias = LBuf_Adopt(atr_pget(victim, A_ALIAS, &aowner, &aflags));
|
||
delete_player_name(victim, alias, true);
|
||
}
|
||
|
||
// Boot off.
|
||
//
|
||
count = boot_off(victim, T("You have been turned into a slimy toad!"));
|
||
|
||
// Release comsys and @mail resources.
|
||
//
|
||
ReleaseAllResources(victim);
|
||
|
||
// #1622: the count goes to the catalogue, not an English suffix.
|
||
buf = tprintf(MN_("%d connection closed.",
|
||
"%d connections closed.", count), count);
|
||
notify_quiet(executor, buf);
|
||
}
|
||
|
||
void do_newpassword
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *password,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref victim = lookup_player(executor, name, false);
|
||
if (victim == NOTHING)
|
||
{
|
||
notify_quiet(executor, M_("No such player."));
|
||
return;
|
||
}
|
||
const UTF8 *pmsg;
|
||
if ( *password != '\0'
|
||
&& !ok_password(password, &pmsg))
|
||
{
|
||
// Can set null passwords, but not bad passwords.
|
||
//
|
||
notify_quiet(executor, pmsg);
|
||
return;
|
||
}
|
||
if (God(victim))
|
||
{
|
||
bool bCan = true;
|
||
if (God(executor))
|
||
{
|
||
// God can change her own password if it's missing.
|
||
//
|
||
int aflags;
|
||
dbref aowner;
|
||
LBuf target = LBuf_Adopt(atr_get("do_newpassword.474", executor, A_PASS, &aowner, &aflags));
|
||
if (target[0] != '\0')
|
||
{
|
||
bCan = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
bCan = false;
|
||
}
|
||
if (!bCan)
|
||
{
|
||
notify_quiet(executor, M_("You cannot change that player’s password."));
|
||
return;
|
||
}
|
||
}
|
||
STARTLOG(LOG_WIZARD, "WIZ", "PASS");
|
||
log_name(executor);
|
||
log_text(T(" changed the password of "));
|
||
log_name(victim);
|
||
ENDLOG;
|
||
|
||
// It's ok, do it.
|
||
//
|
||
ChangePassword(victim, password);
|
||
notify_quiet(executor, M_("Password changed."));
|
||
LBuf buf = LBuf_Src("do_newpassword");
|
||
UTF8 *bp = buf;
|
||
safe_tprintf_str(buf, &bp, M_("Your password has been changed by %s."), Moniker(executor));
|
||
notify_quiet(victim, buf);
|
||
}
|
||
|
||
void do_boot(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if (!Can_Boot(executor))
|
||
{
|
||
notify(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
dbref victim;
|
||
int count;
|
||
if (key & BOOT_PORT)
|
||
{
|
||
if (is_integer(name, nullptr))
|
||
{
|
||
victim = mux_atoi64(name);
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("That’s not a number!"));
|
||
return;
|
||
}
|
||
STARTLOG(LOG_WIZARD, "WIZ", "BOOT");
|
||
log_printf(T("Port %d"), victim);
|
||
log_text(T(" was @booted by "));
|
||
log_name(executor);
|
||
ENDLOG;
|
||
}
|
||
else
|
||
{
|
||
init_match(executor, name, TYPE_PLAYER);
|
||
match_neighbor();
|
||
match_absolute();
|
||
match_player();
|
||
if ((victim = noisy_match_result()) == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (God(victim))
|
||
{
|
||
notify_quiet(executor, M_("You cannot boot that player!"));
|
||
return;
|
||
}
|
||
if ( ( !isPlayer(victim)
|
||
&& !God(executor))
|
||
|| executor == victim)
|
||
{
|
||
notify_quiet(executor, M_("You can only boot off other players!"));
|
||
return;
|
||
}
|
||
STARTLOG(LOG_WIZARD, "WIZ", "BOOT");
|
||
log_name_and_loc(victim);
|
||
log_text(T(" was @booted by "));
|
||
log_name(executor);
|
||
ENDLOG;
|
||
notify_quiet(executor, tprintf(M_("You booted %s off!"), Moniker(victim)));
|
||
}
|
||
|
||
const UTF8 *buf;
|
||
if (key & BOOT_QUIET)
|
||
{
|
||
buf = nullptr;
|
||
}
|
||
else
|
||
{
|
||
buf = tprintf(M_("%s gently shows you the door."), Moniker(executor));
|
||
}
|
||
|
||
if (key & BOOT_PORT)
|
||
{
|
||
count = boot_by_port(victim, God(executor), buf);
|
||
}
|
||
else
|
||
{
|
||
count = boot_off(victim, buf);
|
||
}
|
||
notify_quiet(executor, tprintf(MN_("%d connection closed.",
|
||
"%d connections closed.", count), count));
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_poor: Reduce the wealth of anyone over a specified amount.
|
||
//
|
||
void do_poor(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *arg1, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(executor);
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if (!is_rational(arg1))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Clamp rather than narrow (#1402).
|
||
//
|
||
// The limit is compared against Pennies(), which is int, so a value outside
|
||
// int range cannot describe any wealth a player is able to hold -- an
|
||
// over-large limit reads as "reduce nobody" and must behave that way.
|
||
// `int amt = mux_atoi64(arg1)` instead wrapped, and turned the argument
|
||
// into its own opposite: measured before this change, `@poor 4294967296`
|
||
// truncated to 0 and set *every* player's money to zero, and
|
||
// `@poor 2147483648` truncated to INT_MIN and left every player holding
|
||
// -2147483648 pennies. is_rational() above accepts both, and nothing
|
||
// downstream range-checks, so the wrap was the whole of the arithmetic.
|
||
//
|
||
// Same shape as fun_shl's defeated `0 <= b && b < 64` in #1404: the
|
||
// truncation happens before anything can judge the value. Here there was
|
||
// no check at all to defeat, which is why it corrupted instead of erroring.
|
||
//
|
||
int64_t wanted = mux_atoi64(arg1);
|
||
if (INT_MAX < wanted)
|
||
{
|
||
wanted = INT_MAX;
|
||
}
|
||
else if (wanted < INT_MIN)
|
||
{
|
||
wanted = INT_MIN;
|
||
}
|
||
int amt = static_cast<int>(wanted);
|
||
int curamt;
|
||
dbref a;
|
||
DO_WHOLE_DB(a)
|
||
{
|
||
if (isPlayer(a))
|
||
{
|
||
curamt = Pennies(a);
|
||
if (amt < curamt)
|
||
{
|
||
s_Pennies(a, amt);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_cut: Chop off a contents or exits chain after the named item.
|
||
//
|
||
void do_cut(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *thing, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref object = match_controlled(executor, thing);
|
||
if (Good_obj(object))
|
||
{
|
||
s_Next(object, NOTHING);
|
||
notify_quiet(executor, M_("Cut."));
|
||
}
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// do_motd: Wizard-settable message of the day (displayed on connect)
|
||
//
|
||
void do_motd(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *message, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
bool is_brief = false;
|
||
|
||
if (key & MOTD_BRIEF)
|
||
{
|
||
is_brief = true;
|
||
key = key & ~MOTD_BRIEF;
|
||
if (key == MOTD_ALL)
|
||
key = MOTD_LIST;
|
||
else if (key != MOTD_LIST)
|
||
key |= MOTD_BRIEF;
|
||
}
|
||
|
||
switch (key)
|
||
{
|
||
case MOTD_ALL:
|
||
|
||
mux_strncpy(mudconf.motd_msg, message, sizeof(mudconf.motd_msg)-1);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set: MOTD."));
|
||
}
|
||
break;
|
||
|
||
case MOTD_WIZ:
|
||
|
||
mux_strncpy(mudconf.wizmotd_msg, message, sizeof(mudconf.wizmotd_msg)-1);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set: Wizard MOTD."));
|
||
}
|
||
break;
|
||
|
||
case MOTD_DOWN:
|
||
|
||
mux_strncpy(mudconf.downmotd_msg, message, sizeof(mudconf.downmotd_msg)-1);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set: Down MOTD."));
|
||
}
|
||
break;
|
||
|
||
case MOTD_FULL:
|
||
|
||
mux_strncpy(mudconf.fullmotd_msg, message, sizeof(mudconf.fullmotd_msg)-1);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set: Full MOTD."));
|
||
}
|
||
break;
|
||
|
||
case MOTD_LIST:
|
||
|
||
if (Wizard(executor))
|
||
{
|
||
if (!is_brief)
|
||
{
|
||
notify_quiet(executor, M_("----- motd file -----"));
|
||
fcache_send(executor, FC_MOTD);
|
||
notify_quiet(executor, M_("----- wizmotd file -----"));
|
||
fcache_send(executor, FC_WIZMOTD);
|
||
notify_quiet(executor, M_("----- motd messages -----"));
|
||
}
|
||
notify_quiet(executor, tprintf(M_("MOTD: %s"), mudconf.motd_msg));
|
||
notify_quiet( executor,
|
||
tprintf(M_("Wizard MOTD: %s"), mudconf.wizmotd_msg) );
|
||
notify_quiet( executor,
|
||
tprintf(M_("Down MOTD: %s"), mudconf.downmotd_msg) );
|
||
notify_quiet( executor,
|
||
tprintf(M_("Full MOTD: %s"), mudconf.fullmotd_msg) );
|
||
}
|
||
else
|
||
{
|
||
if (Guest(executor))
|
||
{
|
||
fcache_send(executor, FC_CONN_GUEST);
|
||
}
|
||
else
|
||
{
|
||
fcache_send(executor, FC_MOTD);
|
||
}
|
||
notify_quiet(executor, mudconf.motd_msg);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
|
||
notify_quiet(executor, M_("Illegal combination of switches."));
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_enable: enable or disable global control flags
|
||
//
|
||
NAMETAB enable_names[] =
|
||
{
|
||
{T("building"), 1, CA_PUBLIC, CF_BUILD},
|
||
{T("checkpointing"), 2, CA_PUBLIC, CF_CHECKPOINT},
|
||
{T("cleaning"), 2, CA_PUBLIC, CF_DBCHECK},
|
||
{T("dequeueing"), 1, CA_PUBLIC, CF_DEQUEUE},
|
||
{T("idlechecking"), 2, CA_PUBLIC, CF_IDLECHECK},
|
||
{T("interpret"), 2, CA_PUBLIC, CF_INTERP},
|
||
{T("logins"), 3, CA_PUBLIC, CF_LOGIN},
|
||
{T("guests"), 2, CA_PUBLIC, CF_GUEST},
|
||
{T("eventchecking"), 2, CA_PUBLIC, CF_EVENTCHECK},
|
||
{ nullptr, 0, 0, 0}
|
||
};
|
||
|
||
void do_global(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *flag, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
// Set or clear the indicated flag.
|
||
//
|
||
int flagvalue;
|
||
if (!search_nametab(executor, enable_names, flag, &flagvalue))
|
||
{
|
||
notify_quiet(executor, M_("I don’t know about that flag."));
|
||
}
|
||
else if (key == GLOB_ENABLE)
|
||
{
|
||
// Check for CF_DEQUEUE
|
||
//
|
||
if (flagvalue == CF_DEQUEUE)
|
||
{
|
||
scheduler.SetMinPriority(PRIORITY_CF_DEQUEUE_ENABLED);
|
||
}
|
||
mudconf.control_flags |= flagvalue;
|
||
// Push control_flags (logins/guests/etc.) into the driver's g_dc
|
||
// basket so @enable/@disable take effect on the accept path without
|
||
// a restart. Same callback as cf_live_driver_int.
|
||
//
|
||
if (nullptr != g_driver_config_sync_fn)
|
||
{
|
||
g_driver_config_sync_fn();
|
||
}
|
||
STARTLOG(LOG_CONFIGMODS, "CFG", "GLOBAL");
|
||
log_name(executor);
|
||
log_text(T(" enabled: "));
|
||
log_text(flag);
|
||
ENDLOG;
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Enabled."));
|
||
}
|
||
}
|
||
else if (key == GLOB_DISABLE)
|
||
{
|
||
if (flagvalue == CF_DEQUEUE)
|
||
{
|
||
scheduler.SetMinPriority(PRIORITY_CF_DEQUEUE_DISABLED);
|
||
}
|
||
mudconf.control_flags &= ~flagvalue;
|
||
// See GLOB_ENABLE: keep g_dc.control_flags in sync for logins/guests.
|
||
//
|
||
if (nullptr != g_driver_config_sync_fn)
|
||
{
|
||
g_driver_config_sync_fn();
|
||
}
|
||
STARTLOG(LOG_CONFIGMODS, "CFG", "GLOBAL");
|
||
log_name(executor);
|
||
log_text(T(" disabled: "));
|
||
log_text(flag);
|
||
ENDLOG;
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Disabled."));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("Illegal combination of switches."));
|
||
}
|
||
}
|