info.html · 介绍 · play.html · 游玩 · llms.txt · 下载与本地运行 · ZIP · GitHub
A Spanish-language LPMud, also known as Driade / FFICMUD, run by "Alex" ("Tyrael") in the early 2000s, built on a Final Realms mudlib base with substantial custom changes. A real, substantial multi-year archive with dozens of wizard-built home areas and a large domain tree covering Urghak, Faeleen, Keroon, Asgard, Ishtar, Serkesh, and more, alongside several guild systems including trolls, khurgars, chaos barbarians, and jade knights. New characters register (name confirmation, password, gender) and are born into "Plano Inmaterial" (the Immaterial Plane), a soul-formation antechamber room framing character creation as a literal act of spiritual birth before the soul enters the physical world. Player and staff rank in this world carries its own in-game titling lineage -- ordinary mortals, then ascending ranks named after demigods (semidioses), lords, thanes, patrons, regents, and finally the gods (dioses) themselves. The archive also ships an in-game FTP-like file-transfer feature via a listening network service.
README
A Spanish-language LPMud (also known as Driade / FFICMUD), run by "Alex" ("Tyrael") in the early 2000s, built on a heavily-modified FR (Final Realms) mudlib 3.4 base with pieces of the Spanish Calandria-CLIB derivative. Source cloned from git clone https://github.com/unleashed/ninetears. Slug ninetears, number 939, port 40241.
Despite the target's original framing as an "English-language" archive,
the actual in-game content -- room descriptions, commands, help text,
system messages -- is entirely in Spanish; only the upstream repo's own
README.md (a personal note from the author) is in English. See
NOTES.md for the full porting log.
A real, substantial archive: ~27,000 files / 933K lines under the mudlib root, dozens of real wizard home directories, a large multi-domain world (Urghak, Faeleen, Keroon, Asgard, Ishtar, Serkesh, and more), several guild systems, and archived player save data from the mud's active years.
Registration flow
name -> "Tu nombre [name] es correcto (s/n) ?" -> password -> confirm
password -> gender (h/m) -> lands in "Plano Inmaterial" (a soul-formation
antechamber room).
Admin account
- id:
fluffos - password:
Mud@2026 - privilege: seeded as a "god" (
/global/godplayer-body class,secure/rango.o'sdiosesmapping) plushigh_programmerstatus insecure/master.o'spositionsmapping (full file-ACL). SeeNOTES.mdfor exactly what was edited and why two separate mechanisms needed seeding.
Verified live: special immortal welcome banner on login, [Dios de la
Administracion]-style title, listed under "Inmortales" in who.
Status
Boots clean, zero fatal errors. Master, simul_efun, and every core base
class (std/object, std/room, std/living/living, global/player)
compile with only harmless warnings. A brand-new character registers all
the way into the game world through the real menu-driven flow, and
look/score/who/quit all produce correct output. See NOTES.md
for the full compile-sweep breakdown and the encoding-detection story.
Note: /secure/ftpd is preloaded and opens a real listening socket
on a hardcoded port (4001) a couple of seconds after boot, for an
in-game FTP-style file transfer feature -- see NOTES.md before adding
this lib to any automated high-frequency reboot loop.
WASM status: playable. A uptime() < 10 startup-grace gate in
secure/login.lpc destructed every connection during the WASM
harness's instant-connect window; bypassed for local/loopback
connections only. The preloaded FTP daemon simply fails to compile
without the sockets package (a graceful, non-fatal preload skip).
Full registration/mirar/score/salir verified clean after the
fix. See NOTES.md \S12.
Local run
cd libs/ninetears
~/src/fluffos/build-debug/src/driver config.fluffosGame port: 40241.
NOTES · 移植与修复记录
Ninetears -- porting notes
Source: git clone https://github.com/unleashed/ninetears (commit
4edd22c75e3fa99be4b5820300491c6b62e06085, cloned 2026-08-26). The
repo's own README.md is a short first-person note from "Alex"
(in-game "Tyrael", "Driade's Supreme Deity"), releasing his backup of a
mud he ran "in early 2000's for a couple of years", based on "FR 3.4,
with a few changes from a Spanish mudlib named Calandria" -- "over 1.25
million lines added and about 125k removed in about 28,000 files" per
his own changeset description. Confirmed genuine and worth the full
pipeline: 28,092 files in the raw clone (27,123 under the actual mudlib
root once the bundled driver source and top-level stray files are
excluded), 148MB, real multi-year wizard-authored content, and archived
player save data for real accounts. Slug ninetears, number 939, port
40241.
0. Language mismatch with the task brief -- worth flagging explicitly
The task that produced this port described the target as "confirmed
definitively ... English-language" from an earlier research pass. That
turned out to be wrong on direct inspection: the upstream repo's own
README.md (a personal note from the author) is in English, but every
piece of actual in-game content -- room descriptions, help text, system
messages, comments in the source -- is in Spanish. LEEME (Spanish
for "read me") and LICENCE.fr (also Spanish) sit right next to the
English README.md at the repo root. This wasn't a blocker -- this
project already hosts plenty of non-English archives -- but it did
matter for the encoding-detection step below, since the "pure ASCII
English archive" assumption from AGENTS.md's usual fast path doesn't
apply here; the archive turned out to be a Spanish text encoded in a
Latin-1-family single-byte encoding, not ASCII, not GB18030/Chinese.
1. Root layout: only mudlib/ is the mudlib
The repo root has mudlib/ (the actual mudlib root, matches
mudos/bin/config.fficmud's mudlib directory : /devel/mud/fficmud/mudlib),
mudos/ (bundled MudOS v21/v22 driver source + a prebuilt Windows
binary -- excluded, this project uses its own FluffOS build), and a
few top-level stray files (LEEME, LICENCE.fr, README.1ST,
RUN-HOWTO, and a lone lord.c -- an orphaned wizard-sandbox object
that lives outside the mudlib root and is not referenced from within it,
confirmed by grep). Only mudlib/ was copied into raw/.
2. Encoding: Windows-1252 (Latin-1 family), NOT the default GB18030 --
caught only after a first, silently-wrong conversion pass
The very first convert_lib.sh run used this project's default
GB18030 source encoding (the overwhelming majority of this corpus is
mainland-China GBK/GB18030 wuxia content). GB18030 is permissive enough
that it doesn't error on Latin-1 Spanish-accent bytes -- it just decodes
them as the WRONG (but valid-looking) codepoints, producing silent
mojibake with no error/warning anywhere in the conversion log. Caught
this by manually diffing a converted file's Spanish text against the raw
bytes: room/raceroom.c's "\n\n\xbfQu\xe9 eliges?" (Spanish
"¿Qué eliges?", "What do you choose?") came out of the GB18030 pass as
"\n\n\xe7\xbc\x8cueliges?" -- a real but wrong CJK character
sequence, not an error. iconv -f WINDOWS-1252 (and, equivalently here,
-f ISO-8859-1) on the same bytes produces the correct
"¿Qué eliges?".
A pre-check with a naive grep -rlP '[\x80-\xFF]' initially (and
wrongly) reported zero non-ASCII files -- this turned out to be a
grep -P/locale quirk on this environment (the same command under
LC_ALL=C still reported zero even on a file confirmed by
xxd/Python to contain the exact bytes in question). A direct Python
byte-level scan (any(b >= 0x80 for b in data)) is the reliable check
and found 970 files with high-byte content, consistent with the
converter's own tally after the corrected run (633 clean-decoded +
307 lossy under the wrong encoding, ~940 total -- the small gap is
files whose non-ASCII bytes happened to already be valid, if
coincidental, UTF-8 sequences). Lesson for future archives: don't
trust a grep -P byte-range non-ASCII check on this environment without
cross-checking a positive hit with a direct Python byte scan first --
verify the tool before trusting a "zero" result, especially before
picking a default source encoding.
Re-ran convert_lib.sh libs/ninetears/raw/mudlib libs/ninetears/work
WINDOWS-1252 (the script deletes and recreates work/ from raw/, so
this was a clean re-run, not a patch over the wrong one). Confirmed
correct post-conversion: room/raceroom.c's line above now reads
"¿Qué eliges?" verbatim, and a live boot's welcome banner renders "©
Vilat 2002" with a correct copyright glyph. A handful of directories in
the archive are historically double-saved under both an accented and an
ASCII-transliterated name (e.g. d/faeleen/rooms/rongrond/viñedos_sur/
alongside a separate, independently-existing vinyedos_sur/ with
"ny" substituted for "ñ") -- pre-existing archive content, not touched.
30 files remain non-UTF-8 after conversion, all confirmed genuine binary
media (.jpg/.gif/.bmp/.wav/.tar.gz/.tar.bz2), vim swap files
(.c.swp), or leftover original-server tmp/MudOS_swapfile.* runtime
files -- none of them LPC source, all correctly left untouched.
3. .c -> .lpc rename and reference fixups
22,259 .lpc files after rename. 47,673 literal .c" reference fixups
applied cleanly (0 remaining). 0 angle-bracket #include <...c>
references needed fixing. 9 local angle-bracket includes converted to
quotes (the <local.h>-next-to-includer pattern from AGENTS.md §6.1),
harmless/additive since this archive's headers all resolve through the
single global /include/ directory in the vast majority of cases
anyway -- no master::get_include_path() override was needed; every
#include <foo.h> seen resolves against /include/foo.h directly.
4. static -> nosave sweep
173 files touched. No #define nosave static/#define protected
static compatibility-shim collision found (AGENTS.md §4.3's second
class). No "static string-literal collision found either (the first
class) -- checked via grep '"static' post-sweep, zero hits.
5. Compile verification -- clean on the first attempt
Before running any batch sweep, compiled the small set of widely-shared
base classes individually with lpcc (single-file mode, which also
loads master + simul_efun + preload as a side effect of booting the
compile VM), per this project's RAM-safety discipline:
/secure/simul_efun+/secure/master(loaded together, master is pulled in as a dependency): clean, only adomain_file() in the master file does not work, using bb_ui as fallbackinformational notice -- NOT the mortremains-classexplode("/","/")empty-array crash;master.lpc'screator_file()already has a defensiveif (sizeof(str)<2) return 0;guard, sodomain_file("/")degrades gracefully to a driver-side fallback instead of throwing. The preloaded/secure/ftpdalso compiled and loaded without error./secure/login,/std/object,/std/room,/std/living/living,/global/player: all clean./global/player.lpcproduces six harmless "Redeclaration of global variable" warnings (fsize,topl,fname,finish_func,stat_line,last_search-- all from a/global/more_filemixin whose variables happen to already exist via an earlier inherit in the same file) -- cosmetic, no compile error, no runtime effect observed.
Given this clean baseline, ran the full lpcc_check.sh batch sweep with
RSS watched every ~15s throughout (peaked around 2.9GB, well under the
10GB danger threshold this session's other libs were warned about). The
first pass came back at 8172/22259 (36.7%) -- see §5a below for a real,
fixable shared-root-cause found by triaging the failure log before
accepting that number, and §5b for the corpus's actual composition
(genuine archive gaps, not a driver-compat bug) behind most of the rest.
5a. One real shared-root-cause bug found and fixed: a textually
#included file's global variable landing before an inherit
Per AGENTS.md §6.4 ("one shared root cause, not N bugs"), grepped the
first-pass failure log's error messages by frequency before treating
14,087 failures as unfixable archive noise. error: Illegal to inherit
after defining global variables. (419 raw occurrences) traced back to
exactly two real bugs, both a variant of the classic §6.1 "inherit after
globals" dialect issue that this project's catalog hadn't previously
seen via a textual #include rather than a literal same-file
declaration:
1. /d/gremios/hechizos/base.lpc (the base class every guild spell
in the live spell system inherits) does #include
".../bases/patch.lpc" -- which declares mapping environment_mods;
-- textually *before* its own inherit ".../bases/tipos.lpc";. Since
#include is a textual paste, the compiler sees a global variable
declaration ahead of the inherit statement, identically to the
same-file case, just one indirection removed. This single file is
the base for the guild spell system covered by d/gremios/hechizos/
(spells, items, wizard spells) -- fixing it alone recovered ~340
cascading failures across that whole subtree, this port's version of
mortremains's std/monster.lpc win. Fixed by swapping the two lines
(inherit first, then the #include) -- inherit order is otherwise
unaffected since patch.lpc doesn't need anything from tipos.lpc
before it loads.
2. Eight more d/gremios/hechizos/ files (items/fang.lpc,
wizard/areabas.lpc, wizard/shield_s.lpc,
combined/{acid_arr,burnhand,conecold,det_magi,horrid_w}.lpc) had
the same shape directly (their own global variables -- spell name/
cost/damage-type constants -- declared before their own inherit
".../base.lpc" or .../patch.lpc" line). Fixed each by moving the
inherit line above the file's own variable declarations. (A ninth,
near-identical file, combined/old/acid_arr.lpc, was left alone --
confirmed genuinely dead/orphaned: nothing references the old/
subdirectory, and it inherits a second, entirely nonexistent
/std/spells/patch.lpc regardless of inherit order.)
3. ~46 room files under /d/ss/daggerford/ (a real city zone, not
an antiguo-labeled dead one) all shared: #include "path.h" /
#include "/std/outside.lpc" / inherit CITYROOM;, with
/std/outside.lpc itself declaring nosave string night_long; /
nosave string dark_long; before the room's own inherit CITYROOM
-- the identical bug shape via a different textually-included file.
Reordering alone (moving inherit CITYROOM; above the #include)
traded this error for a second, previously-latent one:
error: Illegal to redefine 'nomask' function 'query_dest_dir'.
-- because /d/ss/daggerford/cityroom.lpc (the file CITYROOM
points at) *already* does inherit "/std/outside"; itself, so the
room's own separate #include "/std/outside.lpc" was a redundant
second copy of the exact same functionality, diamond-inheriting
/std/room's nomask functions a second time once the fatal
ordering error stopped masking it. This driver rejects that
redefinition even though it's a genuine diamond (both paths lead to
the same ultimate file), unlike drivers that silently deduplicate
identical multiple-inherit closures. Root-caused by reading
cityroom.lpc directly rather than patching around the second
error -- it already provides everything /std/outside.lpc does, so
the textual #include in each room file was pure leftover
redundancy (very plausibly predating cityroom.lpc growing its own
inherit "/std/outside" in the original archive's history). Fixed
by deleting the redundant #include "/std/outside.lpc" line
entirely from all 46 files instead of just reordering -- verified
clean on wall1/tim/duke1/delfen individually before trusting
the pattern generally.
Re-ran the full batch sweep after these fixes: 8522/22259 pass
(38.3%), up from the first pass's 8172/22259 (36.7%) -- a net +350
files recovered (fail count 14,087 -> 13,737), consistent with the
combined base.lpc cascade (~340) and the eight direct spell-file fixes,
with the daggerford fix's own recoveries appearing separately since
those 46 files were already broken by the *first* pass's ordering bug
before their diamond-inherit issue was even reachable. No new failures
appeared anywhere outside d/gremios/hechizos/ and d/ss/daggerford/.
5b. The rest of the gap is a genuine messy-archive content
composition, not a driver-compat bug -- spot-checked, not bulk-fixed
Per AGENTS.md's own discipline ("fix the *shared* causes, don't blindly bulk-fix scattered per-file instances in a large archive"), spot-checked the highest-failure-count directories after the fix above rather than chasing the remaining ~13,700 failures file-by-file:
/d/driade(2216 fails, the mud's *own* home domain -- "Driade" is the author's own admin nickname/deity title),/d/turkhun(1926),/d/anduar(1299),/d/keroon(1030), and others: dominated by*Inherited file 'X' does not exist!for base classes that are simply absent from this archive snapshot -- e.g. an entire/baseobs/weapons/directory (referenced by several items'inherit "/baseobs/weapons/dagger"etc.) doesn't exist at all, and/std/underground(referenced by several Turkhun/duergar rooms) has no corresponding file anywhere in the tree. This is the classic "missing zone content is an archive gap, not a bug" class -- the original live server plainly had these files (dozens of active-zone rooms/items still reference them), but they weren't captured in this particular backup snapshot. Not fabricated, per project policy./w/oldcreators(1392) and most other/w/<name>wizard-home fails: genuine 1990s-2000s personal-sandbox content -- abandoned drafts, truncated experiments, copy-pasted-then-modified templates with their own independent typos -- the same *kind* of gap documented onmortremains's wizard-home tree, just proportionally larger here (this archive's overall compile-clean rate is lower thanmortremains's 78.8%, consistent withmortremains's own~13,000messy-content caveat scaling down less favorably on a smaller, messier, single-language codebase)./std/antiguo(866) and/room/antiguo(306):antiguoliterally means "old"/"ancient" in Spanish -- this is the archive's own self-labeled deprecated subtree (confirmed: nothing outsidestd/antiguo/itself references any path under it, e.g. thestd/antiguo/spells/duplicate copies of thed/gremios/hechizos/spell files fixed in §5a are byte-identical dead pre-refactor snapshots). Left alone entirely, matching this project'sadm/AFD/(mortremains)-style precedent for confirmed-orphaned legacy trees.- Common per-file error shapes seen scattered throughout (not bulk-fixed, since no single shared file was responsible): the classic
Type of returned value doesn't match function return type (int vs void)onvoid-declared functions with a barereturn 1;(AGENTS.md's known class, but scattered across dozens of independently-authored spell-item files, not concentrated); the old-MudOSmove("path")single-argument call against this driver's real 3-argumentmove()efun signature on a few non-player-class objects lacking this project's ownvarargs-wrappedmove()override; and the expected large tail of pure mixin files (std/basic/*.lpc) that only compile successfully when inherited into a real container object, never standalone -- an artifact oflpcc_check.shcompiling every.lpcfile individually, not evidence of a real defect.
6. Live boot and full registration -- verified end to end
Booted the real driver (~/src/fluffos/build-debug/src/driver
config.fluffos, cd'd into libs/ninetears first per AGENTS.md §5.2)
and drove it with raw Python socket scripts (not mudclient.py for the
very first probe -- see the test-harness gotcha below).
Test-harness gotcha worth recording: this lib's secure/login.lpc
logon1(str) treats an empty-string first response (str == "")
as TIME_SHORT -- it writes a message and immediately destruct()s the
connection object. This project's usual smoke-test habit of opening with
a blank --send "" (a "press enter to continue past the MOTD" warm-up,
harmless on most libs in this corpus) is genuinely fatal here: it gets
interpreted as a timed-out name prompt and the connection is dropped
right after the banner, with no error anywhere (a real, intentional
destruct(), not a bug) -- confirmed via ss showing the driver-side
socket actively transition to FIN-WAIT-1/local CLOSE-WAIT
immediately after sending a bare newline. This is legitimate original
design (a "did you fall asleep at the name prompt" guard), not a
driver-compat defect, and needed no mudlib fix -- just skip the blank
warm-up line for this lib's own test scripts (start straight with the
character name).
Full flow verified with the blank line removed: fluffos -> s (name
confirm) -> Mud@2026 -> Mud@2026 (password + confirm) -> h (male)
-> lands in "Plano Inmaterial" ("Tu alma se encuentra flotando en la
inmensa oscuridad del Plano Material... una palabra resuena en tu
conciencia: 'nacer'" -- a birth/soul-formation antechamber, matching a
FR-lineage mudlib's traditional pre-character-sheet room). look (shown
automatically on arrival and again on request, consistent both times),
score (HP/Mana/Energy/Social/XP bars, all rendering correctly), and
quit ("Un rayo divino impacta sobre ti..." / "Gracias por jugar, hasta
la proxima.", clean disconnect) all verified. players/f/fluffos.o save
file confirmed created on disk. Zero new fatal errors in debug.log
across the whole session -- only the same six pre-existing
global/player.lpc redeclaration warnings, logged (correctly) to
log/root.log via master.lpc's own log_error()-routing (Root
being global/player.lpc's domain owner per creator_file()).
7. Admin account seeding -- TWO independently-gated mechanisms, both
needed (a new bug-class shape for this project's §1.5 catalog)
This codebase's admin system is unusual among this corpus: rank is not
a flag read at runtime, it's which player-body base class gets
cloned at login. secure/bastards.lpc's query_player_ob(name)
decides between /global/player, /global/creator, /global/lord,
/global/god, etc., and each of those classes hardcodes its own
query_creator()/query_lord()/query_god() to unconditionally
return 1 (e.g. global/god.lpc: nomask int query_god() {return 1;}
int query_lord() {return 1;} int query_creator() {return 1;}). There is
no single "is this account an admin" boolean to flip -- getting fluffos
recognized as a god required edits in three separate places, and
missing any one of them leaves the account only partially privileged:
1. secure/rango.o (a real, restorable save-data file, not
hardcoded source): added "fluffos":" la Administracion" to the
dioses mapping (the same mapping that already lists jade,
barthleby, tyrael). This makes secure/gods.lpc's query_boo()
(which just delegates to secure/rango.lpc's dame_dios()) return a
truthy title string for fluffos.
2. players/f/fluffos.o (the just-registered player's own save
file, real data): changed creator 0 -> creator 1 and
app_creator 0 -> app_creator 1. This step is load-bearing and
easy to miss: query_player_ob()'s entire god/lord/thane/patron
lookup block is gated behind previous_object()->query_creator()
being true *at login time* (if(previous_object()->query_creator()
||flag) { if ("/secure/gods"->query_boo(name)) return "global/god";
... }) -- and previous_object() there is the login object,
whose own creator field only becomes true via
restore_object("/players/.../fluffos",1) picking up a creator 1
line from the player's OWN save file (same-named-variable restore,
not a dedicated admin flag). Without this edit, step 1 alone is
silently ineffective -- the god-table lookup is never even reached.
3. secure/master.o (master's own restorable save data): added
"Fluffos":2 (capitalized, HIGH_LORD) to the positions mapping,
alongside the archive's own pre-existing "sharka":2 entry. This is
a completely separate ACL system from steps 1-2 -- it drives
master.lpc's high_programmer()/query_lord() (positions[
CAP(str)] == HIGH_LORD || HighLords[CAP(str)]), which gates
valid_read()/valid_write()'s top-level "is this euid privileged"
check, i.e. actual file read/write ACL, independent of the in-game
player-body-class rank from steps 1-2. Once fluffos is cloned as a
/global/god instance, move_player_to_start()'s
if (this_player()->query_creator()) seteuid(name); else
seteuid("PLAYER"); gives it the runtime euid "fluffos" (its own
raw registered name, lowercase) -- CAP("fluffos") = "Fluffos",
matching the capitalized key this step seeds.
All three edits were made directly to the .o save-data files (plain
MudOS/FluffOS text-format saves, confirmed safe to edit as text -- no
raw-control-byte encoding in this lineage's save format, unlike
AGENTS.md §7.57's warning class) rather than to source, per this
project's "prefer editing data over code" convention -- none of the
three files needed a code change.
Verified on the NEXT login (a fresh connection, not the same session
that registered the account): a special immortal welcome banner
("Bienvenido, inmortal. Hay mucho trabajo por hacer...") appears
immediately after password entry, replacing the ordinary connect
message; who lists the account under "Inmortales" as Fluffos el
Dios de la Administracion (matching the exact title string seeded in
step 1); score/quit continue to work normally. Did not additionally
verify a file-write-requiring wizard command (e.g. load/update) this
pass -- the who-listed god title plus master.o's seeded
high_programmer status are the two independent signals this project's
own catalog says to check, and both are confirmed; a deeper wizard-ACL
smoke test can be done in a future pass if this lib gets picked up for
further work.
8. A live port collision with a concurrent onboarding session --
resolved before final commit
The originally-assigned port (40240, picked from the highest committed
meta.json port at the time) turned out to be already in live use
by a different, concurrently-running onboarding session's driver
(libs/dreamofseven, a Chinese-language 夢幻七域 lib, not yet
committed at the time and therefore invisible to the usual
grep meta.json collision check). Discovered this the confusing way:
a fresh test connection to port 40240 returned a completely different
mud's (dreamofseven's) Chinese-language banner instead of Ninetears's
own -- traced to ss//proc/<pid>/cwd showing a second live driver
process whose CWD was libs/dreamofseven/work, bound to the same port.
Reassigned to port 40241 (confirmed free against every config.
fluffos file on disk, not just committed meta.json entries, at the
time of reassignment) and re-verified the full boot + registration +
admin-login flow again on the new port before proceeding -- this file's
number/port pair was re-checked against the corpus-wide collision query
one more time immediately before the final commit, per this session's
standing multi-agent coordination protocol.
9. Preloaded FTP daemon opens a real listening socket at boot
/secure/ftpd is the sole entry in secure/master.o's preload list
and is loaded automatically at boot. Its create() schedules
setup_ftp(FR_FTP_PORT) via a 2-second call_out, which does a real
socket_create()/socket_bind()/socket_listen() on a hardcoded
port (#define FR_FTP_PORT 4001 in include/ftp.h) for an in-game
FTP-style file-transfer feature (upload/download to a player's own
directory over a real FTP-protocol connection, judging by the
surrounding PASV/PORT-mode socket_connect() calls elsewhere in the
same file). This is a genuine additional network side effect at every
boot -- an inbound-listening socket, not an outbound connection like
imud/mortremains's Intermud-3 daemon elsewhere in this collection,
but still a real extra open port (4001, independent of whatever main
game port this lib is assigned) that a future automated high-frequency
reboot loop should be aware of, since a stale/colliding process still
holding port 4001 would make this specific feature (not the mud itself)
fail to initialize, and running many copies of this lib's driver
concurrently would only let one of them actually claim port 4001.
10. Outbound network check
No outbound socket_connect()/resolve() call fires unconditionally at
boot or during the compile sweep -- secure/master.o's preload list
has exactly one entry (/secure/ftpd, an inbound listener only, see
\S9 above) and call_out_preload is empty. The Intermud-3 client
(net/intermud3/), gopher daemon, and other net/daemon/ files exist
in the archive but are not preloaded or referenced from any preloaded
file, so they never load unless a wizard manually does so in-game.
11. Verification performed
- Clean boot,
Initializations complete.reached, zero fatal errors across three separate boot sessions (initial encoding fix, admin-data edits, and the final port-40241 re-verification). lpccsingle-file compiles ofsecure/master+secure/simul_efun(together) andsecure/login/std/object/std/room/std/living/living/global/playerindividually: all clean, only harmless warnings, before ever running the full batch sweep.- Full
lpcc_check.shbatch sweep run with RSS monitored every ~15s throughout, well clear of the 10GB danger threshold at all times. - A brand-new character (
fluffos) registered end to end through the real menu-driven flow (name, confirm, password, confirm password, gender), landing in the real starting room ("Plano Inmaterial"), not a void/error state.players/f/fluffos.oconfirmed created on disk. look,score, and a cleanquitall verified producing correct output post-login.- Admin account (
fluffos/Mud@2026) verified on a SEPARATE, later login: special immortal welcome banner,wholisting under "Inmortales" with the exact seeded title string. - Outbound-network check performed (\S10) -- none found; one inbound FTP listener on a hardcoded port documented (\S9).
- WASM status: not attempted (
wasm_statusleft""per task scope).
深度功能测试(§10.7 round two, 2026-08-27)
Full one-continuous-session playthrough per AGENTS.md §10.7, on top of
the onboarding-time smoke test in §11 above. Booted
~/src/fluffos/build-debug/src/driver config.fluffos from
libs/ninetears/, drove it with raw Python socket scripts (the blank
warm-up line is still fatal here per the onboarding note -- start
straight with the character name).
What was tested
- Newbie help:
doc/helpdir/nuevoturned out to be a wizard news bulletin, not player-facing help -- the real onboarding text isroom/raceroom.lpc's own birth dialogue (nacer), read directly from source to learn the test path: race (12 options) -> class (up to 5 per race) -> alignment (9-point axis) -> a point-buy stat screen (sumar/restar caracteristica numero,aceptar,reiniciar) -> a final y/n confirmation. - Full registration through birth, one continuous session: menu flow (name
testespa, confirm, passwordMud@2026/confirm, genderh) -> landed in "Plano Inmaterial" ->nacer-> Humano/Guerrero/ Legal Bueno/16 FUE,14 DES,15 CON,18 INT,14 SAB,8 CAR -> confirmed -- landed in a real starting room with auto-equipped starting gear ("Cota y espada larga"), not a void/error state. look/score/iat every major state change (post-register, post-birth, post-combat, post-quit/relogin) per the checklist -- see the twoscore-crash bugs below, found exactly this way.- Combat and death/respawn: exercised for real, though via an unintended trigger rather than a deliberately-found "safe spar" -- an admin
call move(...) testespateleport landed the test character directly insided/gremios/rooms/khurgars/guildroom, whose guardian NPC (Tharak) attacks any non-duergarliving()onevent_enter()(d/gremios/npcs/tharak.lpc) regardless of membership status -- this is a real, intentional induction-gate/ trespasser design (several humankhurgarmembers exist in the archived player saves, so humans clearly CAN join; my teleport just skipped whatever the intended non-hostile approach is, most likely entering via the room's own "hall" antechamber or talking to Tharak before crossing into the practice hall). Produced a real death (HP to 0, ghost creation, "El alma de Testespa..." sequence, landed as a spirit) and real combat numeric output (varied hit descriptions, damage numbers) -- functional combat and the death-transition message chain both confirmed working code-wise. Left as an honest, untouched observation, not a fix: the resulting ghost kept reflexively fighting Tharak (auto-retaliating, "Haces 0 hp's de danyo desarmado") across aquit+relogin cycle, never reaching whichever full death-god/resurrection sequence this lib has, instead of the ghost either fleeing or the guard disengaging a spirit that can't truly be re-killed. This could plausibly beevent_enter()missing aquery_dead()/ghost check, but I did not confirm it against a *non*-teleported natural death (the only path that reached this state was my own artificial teleport into a hostile-by-design room), so per the scope note in the task brief ("verify a fix doesn't have side effects" / "no error signature = design") this is documented, not touched. A throwaway test character reaching real death organically (rather than via forced teleport into an induction gate) would be needed to tell design from bug here -- left for a future pass. quit, grepdebug.log, reconnect after a real gap: done twice, for two different reasons -- (1) an explicitquitwhile the ghost was still "fighting" (really_quit()'s combat-aware branch,call_out("really_quit", query_level()*foo), correctly reported "Estas en peleas, tardara un poco." and grabbed/saved first); (2) a raw abrupt TCP disconnect with noquitsent at all, specifically to exercise this lib's inline net-dead handling (it has no separatenet_dead()apply -- see the new §7.130 bug below, found exactly this way) -- confirmeddebug.logwent from spamming one crash per heartbeat tick indefinitely to completely clean across a 15-second wait (many ticks) after the fix, and the orphaned body was confirmed actually gone from the live game afterwards (donde testespa-> "Donde esta quien?", not found) instead of lingering forever.- Admin verification: the
fluffos/Mud@2026account seeded at onboarding still logs in correctly post-driver-restart (special immortal banner,creator 1/app_creator 1preserved in its own save file across the whole session) and still has working wizard commands (stat <name>,call fn(...) target) confirmed live. The;<expr>eval shortcut (parse_frogs, verb pattern;*) did NOT work in either; expror;exprform during this pass (fell through to the generic unrecognized-command message both times) -- not chased further sincecallcovers the same need and this is a convenience tool, not a player-facing feature; flagged here in case a future pass wants to root-cause it.
The seven standing cross-cutting bug patterns -- checked explicitly
- §7.121 (float arithmetic in a declared-
intfunction, noto_int()): found and fixed, two independent instances --global/player.lpc'sajustar_xp_necesaria()(crashedscorewith*Array indexes must be integers.past a nonzero XP-required threshold, and would have silently corrupted every level-up's XP deduction into a float too) andstd/tienda.lpc'svender()(a per-sale "already sold N of these, discount the price" factor, the sole float-typed local in a file whose every sibling percentage adjustment correctly stays pure-integer). Both used the no-op(int)xpcast instead ofto_int(xp); both fixed. See AGENTS.md §7.121's now-extended writeup for the full detail. - §8.3a (
private-declared dispatch/callback/call_out function demoted via inheritance): found and fixed, one instance, a new variant shape --refresh2()(therefresh mewizard command's y/n confirmation callback) is declared inglobal/player.lpc, which gets pasted via a textual#includeintoglobal/wiz_file_comm.lpc-- which every admin rank (god/lord/thane/patron/regente/creator) genuinelyinherits. Sincedo_refresh()'s own gate means this command only ever runs for admin accounts, the bug hit 100% of its intended users. Reproduced live (zero output on any response torefresh me, not even the function's own "Pardon?" retry message); fixed by droppingprivate(keptnomask); verified live post-fix. See AGENTS.md §8.3a's extended writeup. - §7.122 (autoload-style class/marker-item duplication on disconnect/reconnect): checked, structurally different, not vulnerable. This is an FR 3.4 base, not TMI-2 -- its own
global/auto_load.lpc(create_auto_load()/load_auto_load()) is a different, unrelated mechanism (noquery_auto_load/compute_autoload_array/destroy_autoload_objnaming at all). Traced the full save/reload lifecycle:really_quit()callssave_me()(which snapshotsall_inventory()into theauto_loadstring array) BEFORE destructing every auto-load-flagged item, matching the documented-safe ordering;do_load_auto()(which callsload_auto_load()) is scheduled exactly once per genuinemove_player_to_start()call, and the force-reconnect-while-still- connected path (secure/login.lpc'stry_throw_out()) re-execs the connection onto the SAME existing live body object rather than cloning a fresh one, sodo_load_auto()never double-fires on one body. No live duplication reproduced or expected from this mechanism. - §7.123 (bare
IDENT = (...);at file scope killing a file's compile): one apparent hit, confirmed FALSE POSITIVE.table/soul_data.lpc(global_adj/soul_data, the social-command "soul" data table) shows exactly this error shape (Type mismatch ( unknown vs string/mapping ) when initializing IDENT) whenlpcc_check.sh's batch sweep compiles it as a standalone file -- but it is textually#included INSIDEobj/handlers/soul.lpc's owncreate()function body (between the{and}), whereglobal_adj/soul_dataare already properly declared earlier in the same file, so the assignments are ordinary in-function statements in their real compilation context. Confirmed via the batch log itself:/obj/handlers/soul.lpc(the file that actually loads at runtime) shows a cleanPASS, zero errors -- this is the "lpcc compiles every file standalone, including mixin fragments that only work when included/inherited into a real container" artifact already documented in this lib's own onboarding NOTES.md §5b, not a real bug. Left untouched. - §7.124 (fraction-vs-percentage unit mismatch on a threshold field): checked, clean. Grepped for
= 0\.[0-9]+;assignments toint-declared fields; zero hits outside dead/antiguocontent. Also specifically checkedwimpy(the auto-flee threshold,std/living/ health.lpc) since it's exactly this shape's classic victim elsewhere in the corpus -- consistently declared and used as an integer percentage 0-100 everywhere (wimpy > 100,hp < max_hp*wimpy/100,(100-wimpy)*i/100), no fractional literal found. - §7.126 (stale pre-
.c-rename extension in saved door/location data): found and fixed, one confirmed instance, a third distinct shape for this bug class. Not an AREA/coordinate-grid engine (this lib doesn't have one) and not a bank-daemon dbase (this lib'sstd/bank.lpcnever callsload_object()) -- instead, a plain player-save scalar field.global/guild-race.lpc'srace_ob/guild_ob/clase_ob/group_ob/race_group_obare ordinary extensionless path strings used as indirect object references (guild_ob->start_player(...)); every setter always persists them extensionless (confirmed: ~250 such fields across everyplayers/*/*.oin the archive, all extensionless except one).players/d/drakzten.o'sguild_obwas"/rol/gremios/khurgar.c"(the file iskhurgar.lpcnow) -- a real pre-existing archive artifact from before this project's own.c->.lpcrename, since.osave data is out of scope forconvert_lib.sh's source-only sed pass. Fixed at the mechanism, not the data: addedstrip_stale_c_ext()/normalizar_referencias_ob()inglobal/guild-race.lpc, called once right afterrestore_object()inmove_player_to_start(), so any current or future save with this shape self-heals on next login. See AGENTS.md §7.126's extended writeup for the general "any scalar object-reference save field, not just AREA doors or bank dbases" lesson this confirms. - §7.129 (
tell_room()/message()wrapper forwarding an omitted arg as a literal0): checked, clean, different mechanism entirely. This lib's owntell_room()simul_efun (secure/simul_efun/modified_efuns.lpc) never touches the nativemessage()efun at all -- it callsevent(ob, "say", str, avoid), a completely different lib-internal broadcast dispatcher (secure/simul_efun.lpc'sevent(), itself just acall_otherloop over"event_"+tipo). The real consumer,event_say()(global/events.lpc), correctly type-checks itsavoidparameter (pointerp(avoid) ? member_array(...) : avoid == this_object()) before using it, so an omitted/int-0avoidis handled safely with no crash. Nomessage()call site anywhere in the codebase was found passing a bare 2-argument call where a 3rd/4th positional argument would matter to the driver's own strictvoid|object|object*check.
New bug found outside the seven standing patterns
- AGENTS.md §7.130 (new entry):
global/player.lpc'sheart_beat()implements this lib's ENTIRE net-dead handling inline (no separatenet_dead()apply exists anywhere in the codebase) -- and its own unconditionallast_command = time() - query_idle(TO);, meant only for the interactive/idle-warning branch, ran on BOTH branches, including right after the!interactive(this_object())branch's ownquit()call (which only schedules a delayedcall_out, not an immediatedest_me()). This crashed*query_idle() of non-interactive object.uncaught on literally every heartbeat tick for a disconnected player, forever, since the crash pre-emptedreally_quit()from ever running -- a genuine resource leak (the orphaned object keeps running/compiling forever), not just log spam. Found via a real raw-socket abrupt-disconnect test (not a code-review guess):debug.logshowed the identical crash recurring against 7+ already-orphaned player-object instances left over from nothing more than ordinary earlier reconnect-testing in the same session. Fixed with a one-lineinteractive()guard; verified both by a cleandebug.logacross many ticks post-fix and by confirming the disconnected body was actually destructed (donde testespa-> not found, instead of an ever-crashing ghost).
Fixes applied (summary, file:line)
1. global/player.lpc, ajustar_xp_necesaria(): return (int)xp; ->
return to_int(xp); (§7.121).
2. std/tienda.lpc, vender(): ajuste=ajuste/(int)ajuste2; ->
ajuste=to_int(ajuste/ajuste2); (§7.121).
3. global/player.lpc, heart_beat(): last_command = time() -
query_idle(TO); -> if (interactive(TO)) last_command = time() -
query_idle(TO); (new AGENTS.md §7.130).
4. global/player.lpc, refresh2(): dropped private (kept
nomask) (§8.3a).
5. global/guild-race.lpc: added strip_stale_c_ext()/
normalizar_referencias_ob(), called from global/player.lpc's
move_player_to_start() right after restore_object() (§7.126).
All five changes verified via lpcc single-file compile (clean, only
the same pre-existing harmless global/player.lpc redeclaration
warnings) both before and after running the §9 LPC formatter on the
three touched files (global/player.lpc, global/guild-race.lpc,
std/tienda.lpc) -- checked all three of §9's known formatter blind
spots (::-split, case+//-comment merge, unbalanced-quote
re-spacing) with zero hits -- and via a full live reboot + re-test of
every fix (score, refresh me, the abrupt-disconnect repro) after
formatting.
Test character cleanup
testespa (Humano Guerrero, the character used for the birth-flow,
combat/death, and quit/reconnect tests) was removed
(players/t/testespa.o deleted) before committing, per this project's
throwaway-test-character policy. Only the seeded admin account
remains in players/ (see below for the rest of this file).
12. WASM status audit (2026-09-01)
playable. secure/login.lpc's logon() had a classic AGENTS.md
§1.3(e) startup-grace gate: if(uptime() < 10) { write(JUST_REBOOTED);
dest_me(); return; }, unconditionally destructing every connection
during the first 10 seconds after boot. The WASM harness connects
instantly after boot, so this fired deterministically on every run.
Bypassed for local/WASM connections only (the same loopback-exemption
pattern used elsewhere in this collection):
if(query_ip_number(this_object()) != "127.0.0.1" && uptime() < 10) {The preloaded /secure/ftpd (real listening socket on port 4001, see
§9 above) fails to *compile* under WASM without the sockets package
(Undefined function socket_create etc.) -- but this is a normal,
gracefully-skipped preload failure (the driver just logs it and moves
on to the next preload file), not fatal to boot, so no fix was needed
there: the FTP feature is simply absent under WASM, matching its
absence in any other sandboxed/no-sockets deployment. After the
uptime() fix, a full WASM session verified clean: registration
(name, s confirm, password + confirm, gender h/m), landing in
"Plano Inmaterial", mirar (look), score, and salir (quit).
商店付费购买(2026-09-04 librarian shop slice)
Round-two §10.7 (2026-08-27) only touched std/tienda.lpc for the
vender() float/to_int fix. This pass exercised a live shop.
Booted ~/src/fluffos/build-debug/src/driver config.fluffos from
libs/ninetears/ on port 40241. Do not send a blank warmup
line -- logon1("") destructs the connection. Admin fluffos /
Mud@2026 logged in straight to /room/raceroom (Plano Inmaterial).
No live clock on the prompt; mudclient.py --idle 6 was only to wait
out the shop clerk call_out("add_dependiente", 5).
config.fluffos still has log directory : /log (absolute). This
boot's debug.log is dead, not clean -- rely on the live transcript.
fluffos save had rp 50 (query_rp()/5 = 10, past the 0–2
"No es bien recibido" refuse) and an empty purse. Wizard
call adjust_money(20,oro) me returned 2000 and i showed
20 monedas de oro.
goto /room/plaza/tienda_discordia landed in Tano's shop. Night
cycle was on: after five seconds, El dependiente sale de la
trastienda murmurando algo sobre los indeseables que le despiertan
a estas horas. look then showed Tano esta aqui.
listar printed a live stock table (capa / armadura de cuero /
brazaletes / guantes plus leftover persisted inventory). Two paid
buys, both with a spoken receipt:
comprar guantes→Tano dice: A ver... Me das 3 oro, 3 plata y 3 cobre, y esto para usted.(list price "Tres oros" = 300 cobre; night*10/9= 333 = 3 oro + 3 plata + 3 cobre)comprar cuero→Me das 1 platino, 1 estanyo y 5 cobre(list "Un platino" = 500; night = 555)
i after both: Cargando : Armadura de cuero y guantes. Purse
11 monedas de oro, 1 moneda de plata y 2 monedas de cobre
(2000 − 333 − 555 = 1112). No programming bug on this path.
ajustar_precio reputation refuse for a 0–2 rp newbie is design,
not a bug -- measure query_rp() before inventing money.
深度功能测试(§10.7,2026-10-04)— negative range ends
scripts/lpc_fix_negative_ranges.py: 3 line(s) in 3 file(s) count a range from the end with < (x[a..-1] -> x[a..<1], x[-2..] -> x[<2..]; KB 06 §7.209). In this driver a negative constant end gave "" / ({ }) and a negative start the whole value, so each of these returned the wrong slice; the compile warning A negative constant as the second element of arr[x..y] is gone from the files that compile. Range lvalues (s[0..-1] = text, the prepend idiom) are left alone. A paired compile of the changed files at HEAD and in the working tree showed no new error.
深度功能测试(§10.7,2026-10-04)— paths named in another case
The archive comes from a case-insensitive host: files are stored as BAGUA0.lpc, longjing.C, d/SHAOLIN/, the code names them in lower
case (__DIR__ "bagua0", "tea/longjing", "/d/shaolin/..."), so the rooms, NPCs and items behind them could not be loaded (a .C file is
not an .lpc file; KB 03 §4.5). scripts/lpc_case_paths.py --convert --apply ninetears: 1 file(s) renamed to lower case (.C -> .lpc,
.H -> .h) and 5 path reference(s) in 5 file(s) rewritten to the new spelling; 0 skipped, 0 ambiguous. Load check of the 9 objects that were renamed, edited or name a renamed path (HEAD against the working tree, an object is the same under any spelling of its path): 8 PASS -> 9 PASS, 0 that loaded before fail now, 1 more load.
1 files moved (1 .lpc); mostly under d/adiel/room (1). A rescan with scripts/lpc_warnings.py ninetears is still due: the renamed files are compiled for the first time.
深度功能测试(§10.7,2026-10-05)— 主控 domain_file("/")
驱动的 mudlib_stats 包在主控载入时调用一次 domain_file("/"),要求返回字符串。本库 secure/master/creator_file.lpc 的 domain_file() 在 creator_file() 没有答案时返回 0,"/" 正是这种情况,所以每次启动都打印 domain_file() in the master file does not work, using bb_ui as fallback 一行,网页版每位访客的终端里都看得到(author_file(<主控文件>) 本来就返回 root uid,不报警)。现在 domain_file() 对 "/" 直接返回 get_bb_uid(),正是驱动缺省时用的值。其他路径不变:创作者的 ls 和 ftpd 直接调用 master()->domain_file(),返回 0 时显示 "Root",所以没有把 0 一律改成字符串(KB 06 §7.222)。用 lpcc config.fluffos /nonexistent.lpc 前后对比,主控载入段只少了这一行。
Deep test (§10.7, 2026-10-08) -- fixed-width .lpc suffix slices
The .c -> .lpc rename changed the string but not the slice width: f[<2..] == ".lpc" compares two characters with four and is never true (!= always true), so suffix filters and strips never ran or cut x.l (KB 06 §7.238). scripts/lpc_lpc_suffix_slice.py --apply fixed 1 comparison(s) and 1 strip(s) (e.g. std/index.lpc); the changed files load at HEAD and in the tree (fresh processes) with no regression.
深度功能测试(§10.7,2026-10-10)— 编译警告整理
scripts/lpc_warnings.py ninetears --fix 加 scripts/lpc_fix_no_effect.py --apply,共改动 1206 个 .lpc 和 6 个 .h(多为删去未用的局部变量)。HEAD 与工作树分别加载(新进程):901 -> 901 通过,无回退;改过的头文件的 23 个包含者 16 -> 16 通过。启动时警告 0 -> 0;新进程登录(输入名字)正常。
登录为西班牙语提示,自动检查认不出,改为手工核对:HEAD 与工作树输入名字后回应完全相同。审计标出的 muro_crumble.lpc 两处是误报:i 仍然声明,删掉的只是未用的 j、str。
深度功能测试(§10.7,2026-10-11):stats.lpc switch 贯穿
std/living/dankequip/stats.lpcset_extra_bonus()与 finalrealms 同码:switch 缺break,设攻击加成时会顺带覆盖防御加成。补break,载入无回退。
深度功能测试(§10.7,2026-10-11):“无副作用表达式”警告复查(二)
逐条复查 64 处 Expression has no side effects / Value of conditional expression is unused(dns_master.lpc 除外),并在全树搜同样的错行(std/antiguo/ 旧副本等),共改 55 个文件:
==当=(10 处):slow_sh.lpc(8 份)counter == 0;使徒手攻击计数永不归零,减速护盾只在第二次攻击时生效一次;d/ss/daggerford/table.lpc(及olddagger/副本)二十一点桌只剩一人时players[1][1]==S_OUT;没把剩下的玩家设为出局。temp + "..."当temp +=(32 处,32 个侦测魔法法术det_magi/det_magic/detectar_magia):剩余时间很长的魔法效果,描述缺最后半句(default分支),玩家只看到“...<名称> .”。- 逗号表达式只清了最后一个变量(15 处,
obj/obsoleto/weapons/ranged/shoot.lpc/throw.lpc/throwold.lpc):me, him, ammo = 0;等改为me = him = ammo = 0;,同 finalrealms。 if ((args,"")==0 || !args)(d/gremios/oldcomandos/codazo.lpc、dobleg.lpc):逗号使条件只剩!args,空串参数漏过语法提示;改为if (!args || args == "")。- 小动物
bandicoot.lpc(3 份同码):1,":grunye.",落在load_a_chat()数组外,和set_level()成了逗号表达式,战斗喊话少一句;移回数组。 ADJ_COST;(wizard/acid_clo.lpc、frost_cl.lpc及std/antiguo/副本,4 处):areabas.lpc里ADJ_COST只是一个从未使用的 int 变量,语句无作用,兄弟法术(fireball、electric)都没有这一行,删去(行为不变)。- 空 if 块(3 处):
templo.lpcreset()里if(!board) { }、punyo_piedra.lpc(2 份)wear_off()里if (target) { }。作者意图无从推断(法术本身只发消息、不设属性),按行为不变删去空 if。
listcheck:55 个对象 HEAD 47 通过、工作树 47 通过,55 个对象载入无回退。8 个失败均为原有:std/antiguo/spells/ 下 7 个旧副本继承不存在的 /std/spells/...;templo.lpc 在无玩家的检查环境中建留言板时 board_handler 的 create_board() 读 this_player() 为 0 而报错(检查环境假象,非本次范围)。
深度功能测试(§10.7,2026-10-12):编译警告收尾
能编译的文件里的警告 1194 -> 0(重扫:lpc_warnings.py,出错文件集合与改前完全相同)。其中 1013 条 Redeclaration of global variable,几乎全部来自一处根源。
- 怪物的两份
spells(956 条):std/living/mon_actions.lpc的触发表mixed *spells与global/spells.lpc的法术表mixed spells同名,obj/monster.lpc两个都继承,每个怪物叶子文件报一次。前者只在本文件里用,改名trigger_spells。与 finalrealms 同码,用lpc_twin_port.py adopt finalrealms ninetears搬来,同批还有global/more_file.lpc的mf_前缀改名(与more_string.lpc的fname/topl/stat_line/last_search等同名)和四个cmd()补varargs。 - 玩家/创作者对象:
global/le.lpc的last_search与more_string.lpc同名,改名le_last_search;global/wiz_communicate.lpc的my_file_name与communicate.lpc同名且从未使用,改名wiz_my_file_name(与 finalrealms 一致)。 - 真 bug:昆虫的体型。
baseobs/races/insect.lpc自己又声明了race_size/limbs,setup()里race_size = 1写进叶子那份,query_race_size()读的是std/races/standard.lpc那份,昆虫一直报人类体型 4。删去叶子的两个声明(limbs本来就另外调用了set_limbs(6))。finalrealms 同改。 - 其他重复声明:
net/nameserver.lpc同一个声明列表里services写了两次;d/anduar/anduar/ciudad/casa.lpc重抄了cityroom.lpc的int n, init=0;;两个 Brethem 酒馆(taberna_noella、taberna_refugio)同一文件里object barman;写了两次;w/oldcreators/zonzamas/armour.lpc、shield.lpc的armour_ac声明两次、armour_type与std/item.lpc同名,按本库obj/armour.lpc的写法处理。 - 宏重定义(45 条):全局头文件
standard.h已定义TP/LEVEL/TO/NAME/ENV,法术旧副本、tweaks.h、net/守护进程、gopher.h/newudp.h等又各自定义,在重定义前加#undef(行为不变,原来就是后定义的生效)。 - 返回类型:怪物假人的
attack_by/attack_ob(10 个文件)、watchman的heart_beat、二十一点桌的dest_me、umber_hulk/vara1的write_message、stalker的start_player、capilla的event_enter、zonzamas 的set_name、psoul2的init_arg都与父类的void对齐(调用方不用返回值);四个locate/rwho命令的query_short_help改为string。工具链修了 9 个原型(get_stat_bit八份、send_udp)。 trap.lpc(两份)先调用后定义的三个函数补原型;inetd.lpc、quest_handler.lpc注释里的/*整理成不改变注释范围的写法;scloud.lpc(四份)把同一个 inherit 写了两行、thieves/hall1.lpc在vguild(已继承 room)前又继承/std/room,由lpc_diamonds.py删去。
未改(文件本身编译不过,记下以后修):
- 28 个公会房间(
room/antiguo/guild_*.lpc、*/barracks.lpc、d/asgard/room/*/sala.lpc等)自己声明object guildmaster;,遮住了std/guildroom.lpc的同名变量:房间把会长复制到自己那份里,而do_join()/do_info()检查的是父类那份,所以一旦这些房间能编译,join/info会永远回答“El guildmaster no está”。它们现在因set_teaching_person未定义而编译不过(本库 guildroom 没有这个函数)。 le_grimbrand5.lpc、curar_en_la_room.lpc同时继承raiseroom(经collect继承 room)和sacroom(继承 room),两份 room 引起 nomaskquery_dest_dir重定义错误;叶子::init()只会走到 raiseroom 的 init,sacroom 的 worship/sacrifice 动作从不加上。d/anduar/*/npcs/rutic*.lpc(8 份)把全局call_outed当临时变量用,遮住 combat 的同名列表。
listcheck:185 个对象(改过的文件、包含改过头文件的文件、继承改过基础类的玩家/怪物对象)HEAD 139 通过、工作树 139 通过,185 个对象载入无回退;删除声明审计 0 条待看。
- 2026-10-14: FluffOS 970d9f09 warns
Float value truncated to int(#1426). 62 sites in 24 files: assignments, initializers and returns now spell the truncationto_int(...)(same value);x op= <float>converted the float before the operation (zeroingxfor a factor below 1) and is nowx = to_int(x op ...):/baseobs/races/shadows/malik_sh.lpc(i *=),/global/spells.lpc(damage *=),/obj/handlers/death.lpc(totaldif +=),/obj/handlers/money_track.lpc(vals[0] +=),/obj/handlers/money_track.lpc(vals[1] +=),/obj/handlers/money_track.lpc(vals[2] +=),/obj/handlers/money_track.lpc(vals[3] +=),/obj/handlers/money_track.lpc(vals[4] +=),/obj/handlers/money_track.lpc(vals[5] +=),/obj/obsoleto/weapons/ranged/ammo.lpc(dam *=),/obj/obsoleto/weapons/ranged/throw.lpc(dam *=),/obj/obsoleto/weapons/ranged/throwold.lpc(dam *=). Changed objects loaded before and after in fresh drivers, no regressions (scripts/lpc_fix_float_trunc.py, KB 04 §6.10).
- 2026-10-14: 162 files did not compile with
Type of returned value doesn't match function return type ( int vs void ): a function declaredvoid(or with no type) returns a value, which the old driver passed to the caller. Functions whose value a caller reads (add_action handlers, commands, helpers) now carry the type they return:add_note(),begin_worship(),bingle_loud_say(),bingle_say(),bingle_shout(),bingle_tell(),bingle_whisper(),casar(),cast_end(),check(),check_damage(),clean_up()…. In driver applies whose value is ignored (init,reset,heart_beat,create) the return is now bare ({ ::init(); return; }keeps a call). HEAD vs tree in fresh drivers: 197 objects, 125 newly loading, 0 regressions (scripts/lpc_fix_void_return.py, KB 04 §6.3).
- 2026-10-14:
std/room.lpcdeclaredadd_item(string str, string desc)while its own docs and the item object (setup_item(mixed, mixed)) take an array of names: the 906 rooms that calladd_item(({"arca", "arcas"}), ...)did not compile. Nowadd_item(mixed str, mixed desc); checked HEAD vs tree on all 906, 612 newly loading, 0 regressions (most of the rest clone/baseobs/weapons/..., which this archive does not have).std/senses.lpcdefinedadd_feel(string ...)against its ownmixedprototype (23 files).obj/handlers/money.lpcupdate_money()indexedpool[dom][type]beforepool[dom]existed, so every NPC with money raisedValue being indexed is zero(caught inadjust_money(), so the distribution table stayed empty); the domain entry is now created first.std/raiseroom.lpcclean_up()isintreturning 0 (what itsvoidreturned). The rutic NPCs' owncall_outedglobal, which hid the inherited one, isrutic_outed;event_enter()overrides back tovoid;stoned.lpcgets asetup_shadow()prototype; leftover bare returns in retyped functions arereturn 0;.
- 2026-10-14: 12 files did not compile with
Illegal to inherit after defining global variables: FluffOS takes everyinheritbefore the first global, MudOS did not care.scripts/lpc_fix_inherit_order.pymoved the globals (and#includes of headers that declare globals) written above theinheritto just below the last one (KB 04 §6.3).
- 2026-10-14 (toolkit round after the add_item fix): rescanning the whole lib turned up 15 more
voidfunctions returning a value; they now take the type they return.event_enter()/event_exit()overrides that the earlier pass had madeintare back tovoid, as in/obj/monster.lpcand/std/room.lpc. Changed objects HEAD vs tree: 14 newly loading, 0 regressions, no warnings left; the whole lib compiles 10,646 files with no warnings in any of them.
Base classes the lib did not ship, restored from sibling libs (2026-10-18, KB 04 6.15)
These base classes are not in the lib, so every object that inherits them (rooms, NPCs, items) failed to load (inherit XXX; with no macro, or inherit "/std/..."; with no file). A sibling lib carries the file and shares many files byte for byte with this one, so the file is copied as it stands: /std/skills <- finalrealms, sample 1/1 load; /baseobs/armours/chainmail <- finalrealms, sample 1/1 load; /baseobs/armours/cloak <- finalrealms, sample 1/1 load; /baseobs/armours/leather <- finalrealms, sample 1/1 load; /baseobs/weapons/bastard_sword <- finalrealms, sample 2/2 load; /baseobs/weapons/dagger <- finalrealms, sample 1/1 load; /baseobs/weapons/hand_axe <- finalrealms, sample 2/2 load; /baseobs/weapons/long_sword <- finalrealms, sample 1/1 load; /baseobs/weapons/mace <- finalrealms, sample 1/1 load; /obj/roomgen/alias <- finalrealms; /obj/roomgen/exit <- finalrealms; /obj/roomgen/item <- finalrealms; /obj/roomgen/misc <- finalrealms; /obj/roomgen/monster <- finalrealms; /obj/roomgen/object <- finalrealms; /obj/roomgen/support <- finalrealms, sample 8/8 load; /obj/roomgen/weapon <- finalrealms; /std/commands/shadows/stun <- finalrealms, sample 1/1 load; /std/living/skills <- finalrealms, sample 2/3 load; /std/newbieguild <- finalrealms, 16 object(s) inherit it, sample 15/15 load; /std/pawn <- finalrealms, sample 2/2 load; /std/pub <- finalrealms, 13 object(s) inherit it, sample 3/13 load; /std/shop <- finalrealms, 27 object(s) inherit it, sample 8/15 load; /std/shop_base <- finalrealms, sample 1/1 load; /std/storeroom <- finalrealms, 33 object(s) inherit it, sample 11/15 load; /std/underground <- finalrealms, 1641 object(s) inherit it, sample 14/15 load. Each base was loaded alone and a sample of its users after it; a base that did not load, or none of whose users did, was taken back (scripts/lpc_restore_bases.py).
Objects that call an inherited function no parent has (2026-10-19, KB 06 7.259)
::create();, ::init(); and return ::buy_object(who, item); were written against a parent that has the function; this lib's parents do not, the old driver took the call for a no-op returning 0, and the current driver stops at compile time with Unable to find the inherited function, so the object never loads. A call written as a statement is deleted, return ::f(...) becomes return 0; (return; in a void function), which is what the old driver did. 5 calls in 5 files: ::race_heartbeat() (5). Only files whose every error was this one were edited; a file that also has another error (usually a parent that is itself missing) is left until the parent is restored. (scripts/lpc_fix_dangling_super.py)
Object paths with two leading slashes (2026-10-19, KB 06 §7.276)
3 file(s), 3 site(s) wrote an object path as //… (like carry_object("//clone/cloth/hic_cloth")): /d/faeleen/npc/braum_ormerod.lpc, /d/faeleen/npc/tabernero_rongrond.lpc, /d/keroon/keroon/keroon_pobre/armeria_guardias_keroon.lpc. The driver these games ran on did not mind (the path was normalised first); this one hands the string to the master's valid_read() as written, securityd looks up //clone, the NPC's euid has no entry and the answer is Read access denied.: the NPC does not load. One slash now.
Standard repair round (2026-10-19, scripts/toolkit_fix.sh)
Rescanned with the current driver; mechanical fixes: 3 functions made varargs (callers pass fewer arguments), 33 unused local variables, 8 files with values returned from void functions. Every changed object was loaded at HEAD and in the tree: no regression.
Hand fixes after the round (2026-10-19, KB 04 6.10)
- The three Duergar guild rooms (
d/newbie/duergar/rooms/guildrm.lpc,room/antiguo/start/duergar-fr.lpc,w/oldcreators/kaerf/duergar/rooms/guildrm.lpc) declaredobject board;over theboardof/std/newbieguildand never read it; the base's own functions clone and use theirs. The dead declaration is gone. std/living/dankequip/living.lpcandliving_creator.lpc:int attack_by(object ob)had noreturnand every caller (ob->attack_by(...)) uses it as a statement; it isvoidlikecombat.lpc's.w/kivara/cavernas4/Sub4_71.lpc,Sub4_91.lpc:inherit SUB4+"sub4_main1.lpc";;lost its second;.- The seven objects load at HEAD and in the tree with no warning; no other warning is left in a file that compiles. 4,347 files still do not compile: 2,469 of them
#include "/d/abarrach/path.h"(and inherit/d/abarrach/handler/base_abarrach.lpc: the header and the base class of an area directory the archive lacks), several hundred more include apath.h/tweaks.hof a wizard directory it lacks, the rest are other errors (undefined names in about 450 files, syntax errors in about 400).
Punctuation the conversion lost, put back (2026-10-19, KB 03 4.6)
2 file(s) lost a semicolon, comma, quote or the closing } at the end of the file and did not load. The file stopped one or two } short (its last byte or two went missing); appended: /room/plaza/base_campo_discordia.lpc, /w/kryger/kryger/base_campo_discordia.lpc. Loaded at HEAD and in the tree: all 2 were failing before, none regressed (scripts/lpc_restore_from_twin.py, lpc_fix_eaten_quote.py --comma, lpc_fix_eof_braces.py).
Old-driver type slack: missing arguments, untyped parameters, array initializers, class variables (2026-10-19, KB 04 6.19, 6.20)
MudOS filled the missing arguments of a call with 0, took an untyped parameter as mixed, never compared a declared type with its initializer and let a variable be named class; this driver refuses all of it at compile time and the whole object fails. 16 file(s) fixed: /d/gremios/hechizos/shadows/stoneskin_sh.anterior.lpc, /d/gremios/hechizos/wizard.old/pf/finger_d.lpc, /d/gremios/hechizos/wizard.old/pf/tell_dem.lpc, /d/gremios/oldcomandos/old/OLDcommands/judge.old.lpc, /d/gremios/oldcomandos/old/bak/judge.old.lpc, /net/daemon/chars/in_finger_demon.lpc, /net/daemon/chars/tell_demon.lpc, /std/antiguo/commands/old/OLDcommands/judge.old.lpc ... (16 in all) (the repair rounds of scripts/lpc_round_files.py: zeros written out at the call, mixed for untyped parameters, string X = ({...}) given its star, void functions returning values, and class variables renamed pick_class). Loaded at HEAD and in the tree, per file: 8 of them failed before and load now, none regressed.
x != "a" || x != "b" and x == "a" && x == "b": comparison chains of one value that can never differ (2026-10-20, KB 06 7.282)
if (arg != "wan" || arg != "pill") return notify_fail(...) is true whatever arg is, dir == "south" && dir == "north" is never true: the guard under the test never opens, or never closes. The author meant "neither of these" (&&) or "either of these" (||). != chains joined by || became && (only where the statement under the test is a return, a refusal or a guard clause), == chains joined by && became || (scripts/lpc_fix_cmp_chain.py). Changed: /std/antiguo/environ/weather_room.lpc (1).
(string) casts of numbers, string variables that hold them (2026-10-20, KB 04 6.22)
The Lathander temple (d/ss/daggerford/lathander/lath4.lpc, lath11.lpc, lathander.lpc: (string)this_player()->query_level() < 5 is (int)) and the twelve backup copies of wimpy.lpc under d/gremios/oldcomandos and std/antiguo/commands (string wimpy holds what sscanf stored: mixed wimpy): 14 of the 15 objects load that never did, none regressed (scripts/lpc_fix_cast_cmp.py). lathander.lpc still fails: set_teaching_person() and add_specialization() are defined by no base the archive has.
member_array('/', str) became strsrch(str, "/"): the character-literal member_array is a byte search (2026-10-20, KB 06 7.283)
'/' in member_array('/', str) is an int, so the driver searches the UTF-8 bytes and answers a byte offset (a third argument is a byte offset too). FluffOS 923cdbc0 warns once per call site (Warning: member_array(int, string) searches BYTES) when the string holds non-ASCII text before the match, and str[0..idx-1] counts characters, so a non-ASCII string was cut in the wrong place. Now strsrch(str, "/") (-1 when absent, a character index; scripts/lpc_fix_member_array_char.py, the JSON encoders and a few other sites by hand). Changed: /room/antiguo/castillo_drakull/npcs/same_obj.lpc (2).
if (x = CONST): a comparison written as an assignment (2026-10-20, KB 06 7.290)
if (arg = "button" || arg = "pill"), if (!arg || arg = "") return 0;, else if (check = 0 && tempB != ""): = was typed for ==, so the condition is the constant: the branch is always taken for a non-zero number or any string (even "") and never for 0, and the variable is overwritten; the driver does not warn. Every such = became == (scripts/lpc_fix_cond_assign.py, 22 terms). Changed: /d/anduar/objetos/scroll.lpc (1), /d/newbie/duergar/rooms/dlev1_01.lpc (1), /d/newbie/duergar/rooms/dlev1_02.lpc (1), /d/newbie/duergar/rooms/dlev1_03.lpc (1), /d/newbie/duergar/rooms/dlev1_04.lpc (2), /d/newbie/duergar/rooms/dlev1_05.lpc (1), /d/newbie/duergar/rooms/dlev1_06.lpc (2), /d/newbie/duergar/rooms/dlev1_07.lpc (2), /d/newbie/duergar/rooms/dlev1_08.lpc (1), /d/newbie/duergar/rooms/dlev1_09.lpc (2), /d/newbie/duergar/rooms/dlev1_10.lpc (1), /d/newbie/duergar/rooms/pr.lpc (2) and 5 more (17 files).
socket / dumpsocket commands: dump_socket_status() is not in this driver (2026-10-20, KB 06 7.291)
These wizard commands do write(dump_socket_status());, a MudOS efun FluffOS lacks, so they did not compile. The simulated efun file now defines dump_socket_status(): the same table from socket_status() (a build without the sockets package, the WASM one, gets the header only; scripts/lpc_add_sefun.py dump_socket_status).
tail command: tail(file) is not in this driver (2026-10-20, KB 06 7.291)
cmds/*/tail.lpc calls the MudOS tail() efun, which FluffOS lacks, so the command did not compile. The simulated efun file now defines tail(file) (the compatible version of the Discworld family: the last 20 lines or so from a full line, 1 when the file exists; scripts/lpc_add_sefun.py tail).
A login password over 255 bytes: crypt() raises (2026-10-20, KB 06 7.294)
The driver's crypt() raises (Error in crypt(), check your salt) for a key over 255 bytes, where the old driver hashed any length. A password of 256 bytes or more typed at the login prompt threw an error in the password check and left the connection at a bare > prompt, so the login never completed (the old-password test of password the same); found by the sweep shape of 300 x. /secure/simul_efun.lpc now defines a simulated efun crypt(key, salt) of the same name: only an over-long key is cut (to 255 characters, then character by character until sizeof(to_buffer(key)) <= 255: the limit is in bytes, a CJK character takes three) before it is handed to efun::crypt(); every ordinary password hashes as before and the saved hashes are untouched (scripts/lpc_add_sefun.py crypt).
旧法术目录里的变量名 in(2026-10-20,KB 04 §6.26)
d/gremios/hechizos/combined/*、wizard/* 等 46 个法术文件把 in 当变量名(syntax error, unexpected L_IN,法术加载不了)。scripts/lpc_fix_in_ident.py 改名为 in_var;名字里带 old、bak 的备份目录没有动(另有 82 个文件在那里,是死副本)。加载检查 46/46,HEAD FAIL → tree PASS。
存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)
存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 2 个存档共 4 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/d/newbie/duergar/rooms/store.o(2 处)、/w/oldcreators/kaerf/duergar/rooms/store.o(2 处)(共 2 个存档)。
add_action() registrations of functions nobody defines (2026-10-21, KB 06 §7.349)
An add_action("fn", "verb") whose function is defined neither by the object, nor by its headers, nor by its parents, nor by any object derived from it (scripts/lpc_scan_callout_targets.py, descendants checked transitively, then function_exists() in a running driver, scripts/lpc_verify_handlers.py). The driver walks the actor's sentences from the newest one: when that sentence's function does not exist it raises *Function for verb 'drop' not found. and tries no other handler of the verb (an older dead sentence is passed over silently). Removed 14 registrations in 11 files (scripts/lpc_fix_dead_add_action.py), the verbs are left to their other handlers: /d/driade/shimmer/brethem/ciudad/alquimia1.lpc (do_read), /d/driade/shimmer/brethem/ciudad/floristeria1.lpc (do_read), /d/driade/shimmer/brethem/ciudad/panaderia1.lpc (do_read), /d/driade/thalos/telgar/keroon/keroon_pobre/bank.lpc (close_account), /d/driade/thalos/telgar/keroon/mercado/keroon_132.lpc (do_read), /d/driade/thalos/telgar/keroon/mercado/keroon_133.lpc (do_read), /d/driade/thalos/telgar/keroon/mercado/keroon_134.lpc (do_read), /d/ss/daggerford/tim.lpc (go_hall), /global/wiz_file_comm.lpc (indent), /std/bank.lpc (close_account) and 1 more files (11 files). Most frequent: do_read x6, close_account x2, go_hall x1, indent x1, hold x1. Renamed: /obj/misc/board_mas2.lpc new -> newx (the file defines newx). Left alone (read): /d/keroon/keroon/mercado/keroon_132.lpc do_read (the object does not compile; the registration waits until it loads), /d/keroon/keroon/mercado/keroon_133.lpc do_read (the object does not compile; the registration waits until it loads), /d/keroon/keroon/mercado/keroon_134.lpc do_read (the object does not compile; the registration waits until it loads).
add_action() registrations of a base whose descendants define the handler: the base itself raises when loaded on its own (2026-10-21, KB 06 §7.349)
A base class (a room, an NPC, a board) registers add_action("fn", "verb") in init() for a function only its descendants define; when the base object itself is loaded (a room one can walk into, an NPC that is cloned) its visitors get *Function for verb 'x' not found. (the first scan passed these over as alive because a descendant defines the handler; scripts/lpc_verify_handlers.py on the base object showed function_exists() MISSING). The registration now stands behind if (function_exists("fn", this_object(), 1)) (flag 1: function_exists() treats protected / private functions as not defined by default); descendants register as before. 8 statements in 3 files (scripts/lpc_fix_dead_add_action.py --guard): /global/communicate.lpc (do_echo, do_echo_to, do_emote_all), /std/living/dankequip/living.lpc (hold, remove, wear, wield), /std/living/living.lpc (remove).
new(MISSING)->move() / carry_object(MACRO(..))->wear() chains guarded (2026-10-22, KB 06 §7.73 addendum)
A statement new(PATH)->move(...); / carry_object(ARMOR_D("x"))->wear(); whose path names no file of this archive: new() answers 0 and ->move() on it raises Bad argument 1 to EFUN call_other(). In create() the object never loads, in reset() every spawn below the first missing one is skipped on every reset. 168 statement(s) in 90 file(s) (new, clone_object) are now { object new_ob = new(PATH); if (new_ob) new_ob->move(...); } (scripts/lpc_guard_carry_object.py --new: the path is resolved with the macros the file itself sees, an ambiguous macro or a virtual-object path is left). Load check of the 90 changed files, HEAD vs tree: 31 load at HEAD, 75 load now (44 newly loading, 0 regress).
log/ subdirectories nobody created: the logging write_file() / log_file() raises (2026-10-22, KB 06 §7.358)
The code of this lib writes logs into 1 subdirectory of log/: log/save/board (global/line_ed.lpc). Neither the archive, a git clone nor the site pack has them (no local boot wrote into them, so gen_keep_dirs.py never recorded them), and write_file() raises *Wrong permissions for opening file /log/dlog/money for append. "No such file or directory" for a missing directory: the function that logs stops on that line and what came after it (the reward, the flag, the next step) never runs. In the eval probe this lib's log_file() wrote nothing (the probe object has no write access and the sefun does not make the directory itself), so what a real caller gets was not measured. log/save/board are now in scripts/wasm_keep_dirs.txt (scripts/lpc_keep_log_dirs.py; the site and the pristine tree create the empty directories from it). Proof (scripts/lpcc_eval.py, an eval object under /adm/daemons or similar writes /log/zzdirprobe/..., fresh processes): the measured table in KB 06 §7.358.
Filo Celestial / Infernal 所有者判定恒真(2026-10-23,KB 06 §7.281 Also)
this_player()->query_name() == "shimmer" || "honny" || …:裸字符串恒真,非所有者也吃到神力 buff。
lpc_fix_literal_or.py 的左侧现识别 name()->…;两把武器各补全比较。
证明:lpcc_eval.py ninetears --at /secure/zzeval.lpc 加载武器;visitor 链为 0,shimmer 为 1。
set_school/sphere 接受数组 + meteoros 缺分号(2026-10-27,KB 04 §6.25 Also)
d/gremios/hechizos/base(及 examples / antiguo 副本)的 set_school/set_sphere 只收 string,而西班牙语法用 ESCUELA ({...}) / ESFERA ({...}) 数组调用,meteoros_diminutos 等无法编译;同文件还缺 inherit 后的 ;。改为 mixed(取首元素),并补上 wizard/mmm 三处分号。证明:HEAD meteoros 失败;改后 meteoros 与 mmm 加载。
深度功能测试(2026-10-27,§6.25 glued inherit / 粘贴残片)
w/leviathan/multi:inherit "/obj/armour" 缺 ;;补上。
深度功能测试(2026-10-27,§6.25 bibliotecario inherit 路径)
w/valar/pruebas/bibliotecario 写成无引号的 inherit /std/monster(syntax error);本库怪物基类是 /obj/monster。改为 inherit "/obj/monster";。证明:HEAD 失败;改后加载(--at /secure/zzeval.lpc)。
深度功能测试(2026-10-27,§6.25 one-arg tell_object 残渣)
score / access_control / vault_room:补 me / this_player() / TP。注释块内 golpear/bash 未动。
深度功能测试(2026-10-27,§6.25 tell_object 参数顺序残渣)
rnd_caus*:tell_object("at the top\n", attackboy)→tell_object(attackboy, …)。
深度功能测试(2026-10-27,§6.25 one-arg tell_room)
tell_room("…") 补房间参数(this_object() / ETO / place / environment(this_object()) 等)。