Discworld MUD (FluffOS bundle, v1)

✅ 可玩

Discworld MUD lib (v1)

dw_fluffos_v1

更新 1309d15 2026-10-22 源码 下载 ZIP

▶ 开始游玩 · Play Now

Cratylus Discworld FluffOS bundle 的最早快照。mudlib 与已转换的`discworld`(v3)同系,单独转档。端口 40271。

English

The earliest of three Cratylus Discworld FluffOS-bundle snapshots in this collection (bundled fluffos-1.22c11 driver ignored). Same official Discworld mudlib as libs/discworld (v3), converted independently from dw_fluffos_v1/lib/ and given the same mechanical FluffOS-compat fixes (virtual.h self-include, add_method("lpc"), .ob #Class suffixes, command.lpc [0..<5], add_action catch-all "*" -> ""). Port 40271.

README

新角色落在 Ankh-Morpork 卷心菜仓库客栈的圆形 Discworld Room。登录 是英文名 + 密码;管理员账号尚未在本快照播种,走普通注册即可。

WASM status: playable. Pumpkin menu + terms + landing in the circular Discworld Room is verified under the shared WASM driver (NOTES.md WASM section). Play: https://mudlibs.fluffos.info/dw_fluffos_v1/

NOTES · 移植与修复记录

dw_fluffos_v1(Discworld bundle v1)-- porting notes

archives/901-1_dw_fluffos_v1_dw_fluffos_v1.tar.gz 是 Cratylus「Discworld Bundle」三份快照里最早的一份。捆绑的 fluffos-1.22c11/ 驱动按 §2 惯例忽略;mudlib 根是 dw_fluffos_v1/lib/(master file : /secure/master)。

原先标 deprioritized,只留 scripts/non_mudlib_meta/dw_fluffos_v1.json。 按用户指示单独转换,不把 v3 的 work/ 当静默 overlay。

convert_lib.sh … GB18030:already_utf8=5945 converted=31 lossy=1 skipped_binary=509;2016 个 .lpc。lossy 是 doc/concepts/conversions(与 v3 相同的 Mac Roman 遗产,见 libs/discworld/NOTES.md)。

从 v3 已验证过的机械修复原样移植到这份独立转换树上(不是拷 libs/discworld/work/):

1. global/virtual/virtual.h:#include "virtual.h" 改回 #include <virtual.h>(convert_lib 本地尖括号启发式自包含)。 2. global/virtual/server.lpc:add_method("lpc", …)。 3. 76 个 .ob 的 ::#Class:: "….c" → .lpc。 4. secure/command.lpc 指令名切片 [0..<3] → [0..<5]。 5. obj/{armour,clothing,shield,clothing_transport}.lpc 的 __FILE__[0..<3] → [0..<5]。 6. 五处 add_action(fn, "*", …) 改成这个驱动认的 "" 万能动词; living/psoul 第三参改成 V_NOSPACE(2)。

另外从 v3 移植的开机阻断修复:

端口 40271。编号 901-1。已确认开机并出现南瓜菜单登录界面 (N/G/角色名)。客串角色创建能走到条款页;完整 look 还要按 v3 那样 把条款 more 翻完。

商店切片(2026-09-04)

英文 Discworld:shop/buy 是本轮角度;没有拜师。端口 40271, ~/src/fluffos/build-debug/src/driver config.fluffos。config.fluffos 的 log directory : /log 是文件系统绝对路径,本机没有 /log,驱动 debug.log 对本 boot 是死的(fd 里没有 log 文件)。以 mudlib 自己的 work/log/runtime / work/log/catch / work/log/error-log 为准。

南瓜菜单 N 注册(shopdive / torchbuy / torchbee,密码 Play2026x),30 秒条款后 yes,落到 /d/liaison/NEWBIE/foyer。 commerce 进新手商店 /d/liaison/NEWBIE/shop。

发现并修复的 bug

std/living/living.lpc 无条件前向声明 protected mixed command(string),在这个驱动上把真正的 command() efun 阴影成空的 FUNC_UNDEFINED。 global/psoul.lpc 里真正的 command() 定义包在 #if !efun_defined(add_action) 里,本驱动有原生 add_action,所以 那个定义被编译掉。玩家对象只剩一个空槽。live:第一次进 commerce 门,exit_move → room_look() → command("look") 报 *Undefined function called: command(work/log/runtime 17:14:17),玩家侧 "A runtime error occurred.",随后心跳停、指令全部 Queued。登录时 move_to_start_pos() 同一条也崩过一次(17:14:04)。这就是 v3 2026-08-27 笔记里把 shop list 标成"排队显示时序问题、未完全验证" 的根因——不是 soul 时序,是每次走门 room_look 都炸。修法与 v3 已有的同一处 guard 相同(import 时没移植过来):把前向声明也包进 #if !efun_defined(add_action)。global/wiz_file_comm.lpc 的 ::command(txt) 覆盖同样在本驱动上编不过 (Unable to find the inherited function 'command'),一并 #ifdef 掉。修复后冷启动:commerce 走门立刻打出商店房间描述, runtime 不再出现 command 未定义。

global/newbie_junk.lpc 的 start_player() 对 ARMOURY->request_item("bucket small") 返回值不判空,链式 bucket->set_short(...)。 这份 distribution 缺 /obj/clothes/ 等 内容目录,request_item 常返回 0(v3 §7.147 同类)。第一次注册的 shopdive 走到商店后 money 是 "You are flat broke."——启动礼包里的 8 Pumpkin dollar / 100 Pumpkin pence 在崩溃点之后,根本没发出去。 修法:先发钱,再对 armoury/clone 返回值逐段判空。修复后新角色 torchbuy/torchbee 登录即 Your purse contains 8 Pumpkin dollar coins and 100 Pumpkin pence coins.

已验证的商店路径

没有拜师(distribution lib 公会内容未随包,见 libs/discworld/NOTES.md 2026-08-27)。

WASM(leftover 655,2026-09-11)

与 v3 discworld 同一套必经桩:dump_socket_status() 挖空(WASM 无 sockets)、compressedp() 常量化、create_dom_creator.lpc 的 compress_file/uncompress_file 改成 no-op。/net/inherit/server.lpc 预加载仍因 socket_create 失败被跳过(BBS,不在登录路径),与 v3 相同。

node /tmp 一次性 walker(scripts/wasm_client.js 语义,条款前 idle 35s,登录后短间隔):南瓜菜单 N → fluffos / Play2026x / male → 条款 yes → 自动 look 落到 /d/liaison/NEWBIE/foyer(cavernous circular Discworld Room,Great A'Tuin / 四象,九个出口含 commerce, womble 在场)。MCCP 提示 "You are logged in uncompressed!" 证明 compressedp 桩。随后 score/quit 进 Discworld 指令队列(登录 no_time_left() + womble 心跳),与 v3 WASM 笔记同一条队列坑;原生 商店切片已验证 score/quit。wasm_status playable。

Do not invent extra pumpkin rooms. mundoscuro stays limited. realms stays noboot.

深度功能测试(§10.7,2026-09-13)

Native FluffOS boot (~/src/fluffos/build-debug/src/driver config.fluffos from libs/dw_fluffos_v1, port 40271). Fresh pass today after the 2026-09-04 shop slice. Account sectestg / Play2026x (pumpkin menu N register → terms yes → foyer). Later reconnect after driver restart: PID 4074608, cwd libs/dw_fluffos_v1/work.

Playthrough evidence

Logs this boot

2026-10-04 — #pragma no_warnings removed

std/effects/basic.lpc carried a mid-file #pragma no_warnings (before query_attrs()); it hid nothing: the file compiles without a diagnostic once the line is gone. The same file, byte for byte, sits in skylib, discworld, dw_fluffos_v1 and dw_fluffos_v2.

深度功能测试(§10.7,2026-10-04)— compile warnings: 249 -> 47

The same edits as libs/discworld (the patch applied unchanged, two files at shifted line numbers; see its section of this date for the list and the reasoning), plus the one line that is only in this tree: include/all_inc.h opened with #pragma save_binary, which the driver ignores and warns about. scripts/lpc_warnings.py: 1695 files compile, 320 do not; 649 distinct diagnostics outside the creator-help examples (249 warnings) before, 447 after: the 400 errors are unchanged and the 47 warnings left are the same as in libs/discworld (33 in the unused std/living/living.eff_shad.lpc, 14 in files that do not compile).

深度功能测试(§10.7,2026-10-04)— include/runtime_config.h was the MudOS-era one

The header numbered the integer settings from 15 where this driver numbers them from 256 (KB 06 §7.89), so get_config(__MAX_BUFFER_SIZE__) answered "" and /net/daemon/http's create() died with Trying to put string in int (line 133) every time it was loaded; the web daemon never came up. The header is the driver's own copy now. The four settings http.lpc reads (__MAX_BUFFER_SIZE__, __MAX_STRING_LENGTH__, __MAX_READ_FILE_SIZE__, __MAX_BYTE_TRANSFER__) are the only symbols the lib takes from it; load_object("/net/daemon/http") answers an object now and the rest of the load-and-clone check is unchanged.

深度功能测试(§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 前后对比,主控载入段只少了这一行。

深度功能测试(§10.7,2026-10-05)— 压缩存档改为明文;board_backups 目录

网页版的 WASM 驱动没有 zlib,restore_object() 只读 <名>.o,打包脚本也排除 *.gz;本库以 .o.gz 存的种子存档(6 个:管理员、bastards、boards、hist_handler、armoury、intermud、rcs_handler)在网页版上全部缺失:文档里的管理员 fluffos / Mud@2026 在网页版上不存在,登录时打印 Failed to restore bastards.。现已 gzip -d 成内容逐字节相同的明文 .o(KB 01)。本机驱动在没有 .o.gz 时读 .o,之后自己的压缩存档会替换它。另外 board handler 启动时把 boards 备份到 /save/board_backups/,目录不存在,每次报 Error copying file;加了 save/board_backups/.gitkeep。验证:本机登录 fluffos / Mud@2026 成功,无 bastards / 复制错误;WASM(解开的 site zip + 明文存档)同一密码登录成功。

2026-10-05 — CONFIG_DB_ERRORS_USER 未定义

obj/handlers/finderror_helper.lpc(finderrors 创作者命令与各领域错误追踪的助手)和 www/secure/creator/bug_top.lpc 用了 CONFIG_DB_ERRORS_USER,公开发布版的 include/config.h 删掉了这个定义,两个文件都编译失败。在全新检出(及网页版)里启动时会载入它,打印 3 条编译错误。config.h 补上 #define CONFIG_DB_ERRORS_USER CONFIG_DB_USER;全新树启动 0 错误 0 警告。

深度功能测试(§10.7,2026-10-08)— .lpc 后缀的定宽切片

.c 改名 .lpc 时只改了字符串没改切片宽度:f[<2..] == ".lpc" 拿两个字符比四个字符,永远不成立(!= 永远成立),按后缀筛文件、去后缀的代码因此从不运行或截成 x.l(KB 06 §7.238)。scripts/lpc_lpc_suffix_slice.py --apply 改了 11 处比较、5 处去后缀(如 www/footer.lpc);改动的文件 HEAD 与工作树分别加载(新进程),无回退。

Compile warnings (2026-10-13)

Same as discworld (f5d0fff16f1): secure/simul_efun.lpc no longer inherits /secure/simul_efun/multiple_short directly (modified_efuns already brings it), and std/environ/weather.lpc's move(arg) is typed mixed. Both load the same at HEAD and in the tree (regress 0). The 33 warnings left are all in std/living/living.eff_shad.lpc, an older variant of living.lpc that nothing inherits or loads (it carries /std/living/effects beside /std/container and calls it by the effects:: scope); left as archived, see the discworld NOTES.

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/effects/healing/basic_cure <- skylib, 1 object(s) inherit it, sample 1/1 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).

Macros the global header lost (2026-10-19, KB 04 6.16)

/include/all_inc.h did not define C_TEMP, C_CLOUD, C_RAIN; the objects that use them (4 on the list) stopped at Undefined variable/Undefined function, and since the compiler stops at the first undefined name the errors and warnings behind it stayed hidden. The sibling libs of the same lineage carry these lines in their headers: the copy of the sibling with the most files in common was added after the last #define of /include/all_inc.h; every path in a macro body exists in this lib. the listed objects have other errors as well and still do not load. (scripts/lpc_restore_defines.py)

The unused living.eff_shad.lpc variant, finished (2026-10-19, KB 06 7.240)

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. 4 file(s) fixed: /d/learning/examples/terrain_map/basic/field_handler.lpc, /obj/fungi/glow_toadstool.lpc, /std/environ/weather.lpc, /std/npc/taliswoman.lpc (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: 2 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). What it was: push lever always answered "You must push an existing lever." (str == "levers" && str == "lever"). Changed: /std/environ/weather_room.lpc (1).

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: /obj/misc/housing_creator.lpc (1).

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, 1 terms). Changed: /cmds/theft_base.lpc (1).

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).

time did not compile: config.h came after the __DISTRIBUTION_LIB__ test (2026-10-20, KB 06 7.312)

cmds/player/time.lpc had #ifndef __DISTRIBUTION_LIB__ #include <special_day.h> #endif before #include <config.h>, the header that defines __DISTRIBUTION_LIB__, so the test was always true and the file included special_day.h, which the distribution lib does not ship: the time command never compiled. #include <config.h> now stands before the test.

Heart beat every two seconds: the command queue never refilled (2026-10-20, KB 06 7.314)

psoul.lpc query_time_left() refills the command budget with time_left += ((time() - tl_updated) / 2) * ROUND_TIME and then sets tl_updated = time(); the player's heart_beat() calls it every beat. The MudOS Discworld beat every two seconds. The driver's default heartbeat interval msec is 1000, so time() - tl_updated is 1, the integer division is 0 on almost every call, and time_left stays at -20: after the first command every other one answered Queued command: ... and nothing ran for minutes (restart and stop did not help; a reconnect to a net-dead body was the same). config.fluffos now has heartbeat interval msec : 2000; live, i, score, date, who and look answer at once in a first visit and after a reconnect.

Player saves need every letter directory (2026-10-20, KB 06 7.315)

save_object() makes no directories, and the Discworld mudlib has no on-demand mkdir for save/players/<letter>/, save/newsrc, save/mail or banish: they existed in the archive only for the letters somebody had played with (not c, d, g, p, s, t), so a character whose name starts with another letter was never written (Could not open /save/players/s/NAME.o.gz.tmp for a save in log/catch at the quit and at every autosave; the login survey said in, saved 0). scripts/lpc_keep_letters.py --apply completed the four buckets in scripts/wasm_keep_dirs.txt (the site loader and pristine_tree.sh make them); live, save/players/s/sweepzq.o.gz is written at the quit. The mortal sweep also needs letters-only names (sweepab) and the N entry of the login menu.

Timed reboot: the mud asked everybody to leave 25 minutes after the boot (2026-10-20, KB 06 7.319)

obj/handlers/garbage.lpc check_reboot() shuts the mud down when uptime() > max_time; max_time is saved in save/garbage.o and shrinks by a sixth at every boot that follows a crash. The committed file had been shrunk by earlier boots to 1491 s, so log/REBOOT said Shutdown requested automatically about 25 minutes after the boot, Offler announced the reboot, logins were refused (It is too close to the shut-down) and the driver exited: on the site nothing starts the mud again. NO_TIMED_REBOOT is now defined (the original ran 61 hours between reboots; the museum has no supervisor). Do not commit the churn of save/garbage.o.

std/shops/print_shop.lpc: a parameter named nosave (2026-10-20, KB 03 4.3)

add_auto_load_info(string nosave, string dynamic) (and the $(nosave) in its body) is a syntax error on this driver (unexpected L_TYPE_MODIFIER): nosave is a variable modifier here, so the print shop base and the shops that inherit it never compiled. The temperature verb of the mortal sweep printed the line (it goes through the shop handler). The parameter is nosave_arg, as discworld (v3) already had it; the three files are identical again.

Creator commands that did not compile: actions_defined, generate_source, mud_list, lpc_info, function_profile, __OPTIMIZATION__ (2026-10-20, KB 06 7.323)

The creator sweep as the seeded admin (play_sweep_lib.py dw_fluffos_v1 --account fluffos:Mud@2026 --dirs creator,lord,playtester --burst 1.3) printed compile errors at the first verb of 13 commands. actions_defined() is called by comment (and the eight objects that inherit cmds/report_base.lpc), which and loc_alcmd; the lib's stub for it sits in secure/simul_efun/add_action.lpc, a file simul_efun.lpc inherits only #if !efun_defined(add_action): this driver has add_action, so the stub was skipped with the file. compile needs generate_source() (the original driver's LPC-to-C compiler), lpc_info needs lpc_info(), prof needs function_profile() (a build option this driver lacks), fry called mud_list() where its own first line says mud_name(), and mudinfo defined __OPTIMIZATION__ only inside its #ifndef __COMPILER__ guard (this driver defines the compiler macro and not the other). New secure/simul_efun/driver_gaps.lpc (inherited by simul_efun.lpc; each stub behind its own #if !efun_defined(x)): actions_defined() answering ({}) as the old stub did (varargs: loc_alcmd passes one argument), generate_source() raising LPC to C compilation is not available on this driver. (compile catches it and prints the message), lpc_info(), function_profile() (an empty profile); the stub is gone from add_action.lpc; fry uses mud_name(); mudinfo has its own #ifndef __OPTIMIZATION__. Gate: 464 objects (cmds, secure/cmds, secure/simul_efun) at HEAD and in the tree, regress 0, 13 newly load; live as the admin which look lists /cmds/living/l_ook and comment, compile, loc_alcmd print without a console line. Still not loading, and not bugs of this port: pstatus (the word broken stands before its #include: the authors disabled it), errors_base2 (MySQL), theft_base and sh_out (headers of areas the distribution lacks), sit (its create() asks this_player(), it loads when a player types it).

存档里的 .c 文件名改成 .lpc(2026-10-21,KB 03 §4.2 第 9 项)

存档里写着 "/d/city/npc/wei.c" 这样的文件名;extract 时源文件已经全部改成 .lpc,而驱动对带扩展名的路径只认字面,new() / load_object() / move() 都找不到它,调用方多半把失败当成「没有这个东西」悄悄跳过。本库 2 个存档共 7 处按 scripts/lpc_fix_save_dotc.py 改成 .lpc(只改 .lpc 存在而 .c 不存在的字符串;玩家存档、备份、留言板、巫师目录和文件索引缓存不动)。改动:/save/armoury.o(5 处)、/secure/master.o(2 处)(共 2 个存档)。

俱乐部控制室 do_transfer():失败分支销毁没赋值的 obj(2026-10-21,KB 06 §7.348)

do_transfer() 在 remove_money() 失败时执行 obj->dest_me();:这是从 do_withdrawl() 抄来的(那里先造了一笔钱 obj,失败时销毁它),do_transfer() 里什么也没造,object obj; 从未赋值。转账失败时抛错,而不是提示 "Unable to remove the money from the account."。去掉声明和那一句。改动:/std/room/inherit/club_control_room.lpc。

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 5 registrations in 1 files (scripts/lpc_fix_dead_add_action.py), the verbs are left to their other handlers: /obj/roommak.lpc (do_add, do_map, do_modify, do_set, ...). Most frequent: do_add x1, do_map x1, do_modify x1, do_set x1, do_setup x1.

Combat probe with the seeded administrator (2026-10-22): 56 of the 87 /std/effects/... paths the lib names do not exist

scripts/play_combat_probe.py dw_fluffos_v1 --account fluffos:Mud@2026 --clone ... --pace 2.5 (Discworld queues one command per heart beat) fights the newbie area's womble: the screen shows *call_other() couldn't find object '/std/effects/ingested/tranquil_sleep'. The whole std/effects/ingested/ directory (drunk 16 references, cum_poison 16, poison 13, basic_healing 6, nausea 5, garlic_breath 4, tranquil_sleep 3, ...), poisons/ (spider_venom 5, poison_gas 3, ...), healing/antiseptic_soap, religious/* and fighting/combat are named by objects but not in the lib; add_effect() / add_eat_effect() of them raises. 31 of the 56 exist in skylib (a Discworld fork): its ingested/ effects load in this tree (basic_healing, blind_protect, calm, cold_cure, garlic_breath, int_bonus, knurd, strength_boost, poison_antidote, ...), the ones that matter most do not as they stand (path.h and the std/shadows/ingested/ shadows are missing here, and skylib's global/standard.h macros ENV / TP / TO / CAP are not defined by these libs). Not changed: a port of about 30 files, queued.

Missing std/effects files adopted from skylib (2026-10-22)

The section above counts 56 /std/effects/... paths that objects of this lib name and the lib does not ship: the womble of the newbie foyer (tranquil_sleep), every drink or herb with an eat effect (drunk, poison, cum_poison, basic_healing, nausea, ...), the scorpion's and the traps' venom (spider_venom, poison_gas), the group command (other/group), consider (it asks PLAYER_DRUNK_EFFECT), the swimming rooms (other/water_effect). libs/skylib is a Discworld fork whose effect core (std/basic/effects.lpc, std/effect_shadow.lpc, the add_effect() / beginning() / merge_effect() / restart() / end() / submit_ee() API, the effect timer's call convention, std/shadows/misc/effects.lpc) is the one of this lib, and it ships most of the missing files (many still carry the original Discworld RCS headers of Ceres, 1998). 51 new files are adopted from it (36 effects, 11 shadows, 4 path.h), all under work/std/; no existing file was edited. Every adopted file starts with // adopted from skylib 2026-10-22: <what was changed>.

Effects adopted unchanged (28): healing/antiseptic_soap, healing/simple_cure, ingested/{basic_charging, basic_healing, blind_protect, calm, cold_cure, cum_poison, garlic_breath, hallucination, int_bonus, knurd, poison, poison_antidote, strength_boost}, object/ward, attached/see_invisible, other/{alertness, dog_urinate, fish_effect, group, ink, meditate_effect, perfume, rubbish_smell, water_effect}, poisons/{constitution, poison_gas}. Shadows adopted unchanged (7): std/shadows/ingested/{blind_prot_shadow, drunk, hallucination, poison}, std/shadows/object/ward, std/shadows/other/fish_effect, std/shadows/attached/see_invisible. New path.h (copies of skylib's): std/effects/ingested/path.h, std/effects/poisons/path.h, std/shadows/ingested/path.h, std/shadows/other/path.h (the std/effects/{attached,object,other}/path.h already existed).

Ports (12 files), each one a token-level edit:

Beyond the first list of 31 (dependencies and live data references, all with a donor in skylib): healing/simple_cure (SIMPLE_CURE of include/disease.h, inherited by antiseptic_soap), poisons/constitution (strength_boost.end() adds it: without it the boost ended in couldn't find object '/std/effects/poisons/constitution'), attached/see_invisible + its shadow (obj/amulets/cat's_head.arm), ingested/vial_healing (obj/food/meals/*.food), object/oak_wand_healing (obj/plants/yarrow.ob, thorny_burr.ob, angelica_root.ob).

Evidence.

Still missing after this (no donor in skylib or in any other lib of this repository; nothing was invented): fighting/combat (named by disease/rabies, room/albatross_attack, living/skills, obj/handlers/taskmaster), poisons/{angry, bloat, con_new, confuse, hp_new, smokebomb} (obj/state_change/base.hdr_dc.lpc and the heat / distill / press .dat files: the cooking and distilling poisons; skylib's poisons/hp is the old potion-space file, not hp_new), religious/{basic_ritual, blind, shield, theological_debate} (debate command, obj/handlers/philosophies, std/living/armour, std/shadows/misc/poker_shadow), magic/{invisibility, virtual_spell} (obj/rings/invisibility, include/virtual_spell.h), disease/{broken_leg, jellyfish_sting} (obj/misc/traps/leg_trap, std/races/jellyfish; skylib has a curses/broken_leg under another path), the faith/*, magic/{magic_circle, grabbing_hands, reduced_weight, witch_eye} and object/rubbery_weapon names that only secure/rcs_handler.o (an RCS file list) carries. Donor exists but only a backup (obj/reactions/base.hdr_dc.c.old) or a doc names them, so not adopted: external/fungus_cure, healing/cold_remedy, healing/weak_hp, ingested/{frog_repellent, potion_spellcheck, weak_poison_cure}, magic/magic_spell. ingested/query_drunk_level() does not exist in skylib's drunk (consider asks it for the considerer's drunkenness): the call returns 0, so the drunk-considerer lines of con_sider.lpc never show.

Fixed on the way (outside the effect files, same day):

Seen on the way, not fixed (outside the effect files):

clone_object() of a shadow the lib never shipped, split over two lines: the -> chain guarded (2026-10-22, KB 06 §7.73 addendum)

std/object.lpc init_dynamic_arg() restores a "special" item with clone_object( "/std/shadows/object/special_item" )->\n setup_shadow( this_object(), player );: the file is not in this archive (std/shadows/object/ has the other shadows), clone_object() of it raises for the object that carries the special dynamic argument. The statement is now { object new_ob = clone_object("/std/shadows/object/special_item"); if (new_ob) new_ob->setup_shadow( this_object(), player ); } (scripts/lpc_guard_carry_object.py --new). The earlier chain-guard pass never reached this lib: the --new regex took exponential time on discworld's source (identifier tokens could be split anywhere) and the run was abandoned; with a word boundary after each identifier the scan of the lib takes 2 seconds. Load check of the changed file, HEAD vs tree: loads in both (the statement sits in a branch that only runs for a saved special item).

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 4 subdirectories of log/: log/errors (net/intermud3/services/error.lpc), log/http (net/daemon/http.lpc), log/log/secure/playtesters (obj/handlers/library.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/errors, log/http, log/log/secure/playtesters, log/secure 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.