Commit graph

17 commits

Author SHA1 Message Date
Stephen Dennis
9a4de32dcb fix(queue,db): scheduler enqueue success and flatfile write failures (#1871 #1869)
DeferTask/DeferImmediateTask return bool (nothrow OOM); wait_que and
sql_que free the BQUE and refund quota/waitcost when enqueue fails.
db_write_object reports stream status via ferror; db_write returns -1
on I/O failure after header/object/end/flush checks, and dump/dbconvert
callers refuse to publish a truncated flatfile.
2026-07-31 09:09:18 -06:00
Stephen Dennis
2285fa54be chore(int64): quota, charges, queue counts stay full width after mux_atoi64 (#1402)
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.
2026-07-28 19:40:41 -06:00
Stephen Dennis
08ee03ba52 nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:

  cque         @ps timer labels, WaitQ warp, commands processed
  command      switch errors, dig/open/create/kill costs, money help,
               queue/login limits, lua/cache stats display
  predicates   not-enough-money, command table add/not-found
  mail         folder/send/clear/reject/stats *sentences* only

Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 15:01:48 +00:00
Stephen Dennis
6b2e1bf753 nls: convert remaining count plurals to MN_() (#1661)
Three player-visible notify sites still computed English morphology in C
and passed it as %s — the #1622 shape:

  %d queue entr%s removed.          y / ies   (cque, two sites)
  %d orphaned attribute name%s purged.  "" / s  (@dbclean)

MN_(singular, plural, n) sends the count to the catalogue.  Regenerates
the pot and fills xx.po; ko.po leaves the new entries untranslated.
2026-07-28 05:12:03 +00:00
Stephen Dennis
a30a55332f nls: mark wiz/cque notify prose with M_ (#1419)
Next notify slice: teleport/force/wall-style wiz chatter and queue/PID
messages (~57 sites). Leave tprintf formats as T(). Half-mark pass clean.
Regenerate pot/xx without fuzzy entries.
2026-07-27 11:47:18 +00:00
Stephen Dennis
b5ee3b6f81 refactor: use UTF-8 text in user-facing string literals (#1513)
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.
2026-07-26 21:52:48 -06:00
Stephen Dennis
2f106f200f fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for.  mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows.  Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.

Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class.  A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.

Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64.  No
control flow, no types, no behaviour beyond the wider parse.

This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged.  It only widens the parse on
Windows.  Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.

Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it.  Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.

Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:12:35 -06:00
Stephen Dennis
7bc1ac2a01 fix: resolve Pass 3 High audit defects #1077–#1080
#1080 queue: refund payfor cost on runaway QueueMax path (match OOM).

#1078 jit: guest_range_ok for PIN_ARRAY/UNPIN; no wrap on addr+len*8.

#1079 jit: ecall_invoke_fun loads fargs via guest_farg_addr + guest_strnlen;
overflow-safe out buffer check.

#1077 convert: EncodeAttrValue uses sized growable buffer; PASS uses
std::string; lock Write gets Append/WriteString (no stack sprintf).

omega builds; smoke 1319/1319; JIT q-reg oracle OK.
2026-07-24 12:49:34 -06:00
Stephen Dennis
4d6fac656c fix: resolve 2.14 audit defects #1039–#1061
Restart (#1039–#1043): zero process-local DESC fields after load,
restore peer via getpeername, strip non-survivable TLS/WS flags,
drop TLS and WebSocket before exec, tear down adopt failures with
shutdownsock, abort partial restart loads cleanly.

DB (#1044–#1047): cache_flush_writes returns success only after
Commit; leave dirty queue on failure; atr_head union SQLite with
pending cache/queue; set mudstate.bStandAlone in DbConvert; flush
attr queue before import Commit.

Queue (#1048–#1052): setup_que refunds on OOM; sql_que frees BQUE
on Query fail and inits waittime; do_wait rolls back semaphore if
wait_que fails; include nest depth limit 50; CPU halt_que uses
Owner+object for machines.

JIT (#1053–#1057): full 64-bit rv_load_i64; setq/r single-char
RegisterSet; decline long CARGS; accumulate inline u() watermarks;
bounded guest_strnlen on ECALL paths.

Conf/convert (#1058–#1061): @enable/@disable sync g_dc; live
driver knobs for output/retry/max_players/timeouts/quota; safe
heap encode for t5x/t6h/r7h attributes.
2026-07-24 08:26:59 -06:00
Stephen Dennis
30c07946e3 perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.

Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics.  Exactly one copy problem existed:

iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes.  Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB.  Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.

Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.

Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
Stephen Dennis
7acf4c9e7b fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.

Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).

This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.

No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.

Closes #896.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
Stephen Dennis
fcae41511b Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):

 - @clone/cost on an exit bypassed the "must control current location"
   check (it lived only on the non-/cost path), letting a builder splice a
   cloned exit into a room they do not control. (#855)
 - The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
   which does not exist (produces "Huh?"); corrected to @mark_all/clear.
   (#856)
 - @ps <object> reported nothing for a controlled object owned by another
   player; do_ps was missing the non-player-target else clause that the
   sibling @halt has (clear the owner filter). (#857)
 - whisper "<quoted name>" skipped the locality/connected gate the unquoted
   form applies, giving a success confirmation plus a delivery error and
   polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
   did not advance the parser. (#858)
 - @flag/remove of an unknown/empty flag name was silent; now reports an
   error like the other flag-name failure paths. (#859)
 - report help said 8-hour segments but the code uses 4 (deliberately, per
   4a845139f); corrected the help. (#860)

Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
Stephen Dennis
f068c5be21 engine: @dolist/now/break switch; ;| piping in inline command lists (#788)
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>
2026-06-10 09:00:21 -06:00
Stephen Dennis
900e3e3175 Convert ~75 atr_get/atr_pget sites to LBuf_Adopt across 17 files
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>
2026-04-05 18:34:51 -06:00
Stephen Dennis
3b62628772 Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8).  Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
Stephen Dennis
369f92495c Remove ISOUTOFMEMORY macro; add per-site OOM recovery and fix g_dump_child_pid
Eliminate the ISOUTOFMEMORY macro that unconditionally aborted on allocation
failure. Each of the 23 call sites now handles OOM appropriately:
- Fatal sites (buffer pools, db array, anum table): mux_assert or OutOfMemory
- Recoverable sites (queue, mail, commands, guests, vattrs, config, restart,
  forward lists): log the failure and return gracefully

Also fix g_dump_child_pid portability: volatile pid_t -> volatile sig_atomic_t
with explicit casts in ganl_adapter.cpp.

Close integer overflow issue as false alarm (getstring_noalloc uses a bounded
static buffer, so nBuffer+1 cannot wrap).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:31:20 -06:00
Stephen Dennis
4ff1398de1 Restructure mux/ directory: component-based layout with proper build root
Move from flat mux/src/ layout to clean component hierarchy:
- mux/ is now the autoconf/automake build root (configure.ac lives here)
- mux/include/ — shared headers used by multiple components
- mux/lib/ — libmux.so (core utilities, no game state)
- mux/src/ — netmux driver only (thin networking shell)
- mux/modules/engine/ — engine.so (game logic)
- mux/modules/{comsys,mail,exp3,sqlproxy,sqlslave}/ — external modules
- mux/ganl/ — GANL networking library
- mux/sqlite/ — SQLite amalgamation (builds libsqlite3.a)
- mux/announce/ — announce tool (was mux/src/tools/)

Build changes:
- SUBDIRS ordering: ganl sqlite lib src modules announce
- libmux.so gets -Wl,-soname,libmux.so; netmux links via -L -lmux
- engine.so links libsqlite3.a and libmux.so with -Wl,--no-undefined
- RPATH uses $ORIGIN for portable .so resolution
- Install hooks use absolute paths for game/bin symlinks

Bug fixes:
- engine.so mux_Register() now passes nullptr to mux_RegisterClassObjects
  (matches all other modules; libmux already has the factory via dlsym)
- DbConvert() now calls pcache_init() before db_write, fixing a latent
  crash (free(): invalid pointer) when exporting from SQLite databases

411/411 smoke tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 20:38:37 -06:00
Renamed from mux/src/cque.cpp (Browse further)