Commit graph

14 commits

Author SHA1 Message Date
Stephen Dennis
bc4cec252e fix(nls): plural morphology baked into msgids, and a fragment that could not be translated (#1717)
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>
2026-07-28 21:31:32 +00:00
Stephen Dennis
5cee6cb721 feat(tables): staff list layouts via mux_table (#1667 Phase 4 C*) 2026-07-28 11:31:23 -06:00
Stephen Dennis
940148dbb6 nls: mark walkdb/cron/conf search-and-admin notify prose with M_() (#1683)
Phase 3 coverage, away from the mux_table lane:

  walkdb  @search/@chown/@mark, universe stats, coin checks
  cron    remove/match counts via MN_(), list/bad-entry lines
  conf    config thing not found / already exists (notify path)

Search-class keywords, softcode tokens, and logs stay T(). pot grows;
xx.po rebuilt clean for complete catalogue; ko msgmerge only.
2026-07-28 15:15:30 +00:00
Stephen Dennis
0573884ca3 nls: mark walkdb/levels/walk notify prose with M_ (#1419)
Notify slice: @search/@find-style and reality-level/walk chatter
(~35 sites). Leave tprintf formats as T(). Half-mark pass clean.
Regenerate pot/xx without fuzzy entries.
2026-07-27 11:50:11 +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
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
37e18bef52 engine: make @dolist/now run multi-command bodies and honor @break (#765)
@dolist/now executed each iteration's body via a single process_command(),
so it could not split a ';'-separated body and never honored break_called.
Worse, running the body inline could mutate the executor's own attributes
and free the lbufs the 'list'/'command' arguments pointed into, corrupting
the walk so the loop stopped after the first iteration. Net effect: the
"@break stops the loop" idiom the assert_cmd tests wanted was impossible,
and @dolist/now over an accumulating body silently truncated.

do_dolist / bind_and_process now (for the /now path):
  - iterate over private copies of the list and command so inline body
    execution can't pull the rug out from under the walk,
  - run the body as an action list (split on ';', honoring break_called),
    mirroring do_include(), and
  - stop the loop when @break/@assert fires, while saving/restoring
    break_called so the break is contained to the dolist and does not leak
    into the surrounding command list.

The queued (default) @dolist path is unchanged in behavior.

Adds assert_cmd TC005/006/007 to lock in the inline semantics: break
stops the loop (-> "a"), a non-firing loop records every element
(-> "a b c", the truncation regression guard), and an inline break stays
contained (a command after @dolist/now in the same list still runs).

Verified: full smoke 1081 ok / 0 new failures; standalone repros confirm
break->"a", no-break->"a b c", and outer-command-still-runs. Closes the
follow-up noted in #765.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 14:44:36 -05: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
b70214682a Migrate 16 more alloc_lbuf/free_lbuf sites to LBuf RAII
Third wave: convert manual alloc/free pairs in walkdb (3), htab (3),
dbt_spike (1), plusemail (2), mguests (2), file_c (2), powers (1),
jit_compiler (2).  Completes all convertible sites in the small and
medium engine source files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:04:46 -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
dc4d5228e2 Convert walkdb object list to STL 2026-03-22 04:39:27 +00: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/walkdb.cpp (Browse further)