Maintainer direction: prefer int64_t over long/int for parsed softcode
and attribute integers so LLP64 and LP64 behave the same.
This first slice covers the leftovers called out on the issue:
- quota: store/show/set paths (avoids @quota/all 2^32 writing 0)
- A_CHARGES in set/levels/predicates (RUNOUT vs wrap-to-positive)
- semaphore counts and @ps/queue kick/warp parsing
- IDLETIMEOUT attribute seconds
Bounded API edges (RunTasks(int), optional int *pnum) clamp after the
full-width parse. pot: quota notify formats use %lld.
Continue Phase 3 after the rob slice: mark player/staff tprintf,
mux_sprintf, and raw_broadcast format templates with M_() so whole
sentences like "%s created as room #%d." enter the catalogue.
Left as T(): softcode machine templates ("#%d") and the ANSI color
assembly ("%s%s%s") — not notify prose. Log-only "Strange object type"
stays T() as well.
Regenerate pot/xx/mo without fuzzy entries. Format and NLS guards green.
Next notify slice: attribute/name/zone/lock/wipe player messages in
set.cpp (~73 sites, 54 unique msgids). Leave tprintf formats and the
@notify/quiet me command string as T(). Promote one half-mark
(did_it/default twin). Regenerate pot/xx (~113 catalog msgids).
Replace ~900 typographic \xE2\x80\x.. escapes (curly quotes, en dashes)
and a few \xE2\x80\230 octal workarounds with real UTF-8 in message
strings under mux/modules and mux/src. Leave stringutil and convert
charset mapping tables as explicit byte sequences.
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pass 12 (C3): do_include required only See_attr but executed the attribute
body as the source object via process_command_list_inline(thing, ...).
That is stronger than @trigger (Controls) and contradicts the comment that
include expands into the caller's action list.
Pass the includer as executor. Smoke TC011 locks VISUAL foreign-library
include so @set me= applies to the includer.
Closes#1279. Related #1280 (NOEVAL permit/ignore footgun, filed only).
Two follow-ups to the @dolist/now rewrite (37e18bef5):
- New /break switch: an @break inside the body still stops the loop,
but with /break it also propagates to the enclosing command list,
aborting the commands after the @dolist. This restores the one
ability the containment change removed (using a break inside the
loop to abort the rest of a $-command), with sane loop-stop
semantics, and mirrors @include (propagate by default, /nobreak to
contain). Without /now the switch is inert.
- ;| pipe segments now work in inline command lists. The queued
runner has always special-cased '|' after ';' to capture the
previous command's output as %|; the inline loops in @dolist/now
and @include treated such a segment as a literal command starting
with '|' ("Huh?"). The splitting/pipe/break loop is factored into
process_command_list_inline() in cque.cpp, shared by both callers
and kept next to its queued twin so the semantics cannot drift.
The inline list is its own pipe domain (save/restore of the
enclosing pipe context), matching how each queued entry starts with
a clean pipe state -- an inline list running inside a piped segment
cannot corrupt the outer capture.
help @dolist documents /break and the ;| support.
Verified: default containment, /break propagation, and /break with no
inner break all behave as specified; pipes carry %| through inline
dolist and include bodies; smoke 1115/0/0 with three new test cases;
2x200 jit_diff clean.
Closes#788.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use the new LBuf_Adopt() macro to take RAII ownership of caller-owned
pool buffers returned by atr_get, atr_pget, and atr_get_LEN. This
eliminates ~82 explicit free_lbuf calls and automatically covers
early-return paths that previously required careful manual placement
of the free.
Heaviest conversions: player.cpp (13), comsys.cpp (14), command.cpp (9).
Complex interleaved patterns (did_it charge/runout swaps, PureName
reassignment, process_cmdent loops) left manual for now.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Partition the routing graph by Zone(room). Each zone has an independent
BFS table and generation counter, so topology changes in one zone do not
force rebuilds elsewhere. Rooms with no zone (NOTHING) are grouped into
a synthetic orphan zone.
Cross-zone routing uses a gateway-edge meta-table: exits that cross zone
boundaries are collected as GatewayEdge structs, and queries between
different zones run BFS over the zone graph to find the gateway chain,
then use local tables for intra-zone segments.
New API: route_invalidate_zone() and route_invalidate_meta() for
targeted invalidation. do_chzone() now invalidates both the old and new
zone plus the meta-table when a navigable room changes zones.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>