Formosa Saga

✅ 可玩

源流福爾摩沙

formosasaga

更新 fdf581a 2026-09-12 源码 下载 ZIP 上游 wade-fs/FormosaSaga

▶ 开始游玩 · Play Now

A Taiwan-history exploration MUD (源流福爾摩沙) by wade-fs. New characters pick a language, id, password, nickname, race, and nature; the first player names the mud and becomes god, then starts in 民雄鄉 (Minxiong / 打貓社), a Chiayi township built as layered historical sites rather than square rooms. Play is Traditional Chinese (look/info/quit); travel uses place names (民雄車站, 大士爺廟, 甘蔗田). No MySQL required.

README

A Taiwan-history exploration MUD cloned from https://github.com/wade-fs/FormosaSaga (commit a700cf5, 2026-06-28). This collection’s copy is a snapshot as of 2026-09-05, not a live mirror. Slug formosasaga, number 967, port 40276.

Upstream is a Go MudOS-v22 simulator with a browser client. This project runs the mudlib on FluffOS telnet. In-game text is Traditional Chinese (look / info / quit). New characters start in 民雄鄉 (Minxiong / 打貓社) and travel between named historical sites.

Registration flow

Language 1 English / 2 繁體中文 / 3 简体中文 → id → password → nickname → race (14) → nature (14). The first player names the mud and becomes god.

Admin account

Verified live: 2 → fluffos / Mud@2026 → 秦風 in 打貓社; info shows level 1; quit saves and returns to the language prompt.

Local run

cd libs/formosasaga
~/src/fluffos/build-debug/src/driver config.fluffos

Game port: 40276. Setup after password can take several seconds.

python3 scripts/mudclient.py 127.0.0.1 40276 --timeout 70 --idle 8 \
  --send "2" --send "fluffos" --send "Mud@2026" \
  --send "look" --send "info" --send "quit"

NOTES · 移植与修复记录

源流福爾摩沙 (formosasaga) — porting notes

Source: git clone https://github.com/wade-fs/FormosaSaga (commit a700cf5de8266868d574eb374e0b11da0b5877f5, “整理 python scripts”, 2026-06-28; converted 2026-09-05). Config name 源流福爾摩沙. Slug formosasaga, number 967, port 40276.

This collection’s copy is a snapshot, not a live mirror. The upstream tree is a Go MudOS-v22 simulator plus a browser/HTTP client (port 8080). This project runs the mudlib on FluffOS telnet. Local driver-compat shims mean it has diverged from upstream.

Unique original Taiwan-history LPC lib. Not a derivative of any collection game. Do not onboard the repo again.

0. Layout

Upstream repo root is the Go driver + mudlib/. Only mudlib/ was rsync’d (exclude .git) into raw/ then convert_lib.sh. Do not copy .git into work/ (submodule trap).

raw/ is gitignored. Do not re-clone or re-convert.

Master /master, simul_efun /secure/simul_efun. World data is ~3500 YAML files under data/yaml/ (converted in-place to JSON so yaml_decode can be json_decode). Start room /area/settlements/minxiong.lpc (民雄鄉 / 打貓社).

Original connect(string lang) is not FluffOS connect(int port).

1. Convert (2026-09-05)

convert_lib.sh UTF-8: already_utf8=3785 converted=0 lossy=0 skipped_binary=9. 258 .lpc. 449 .c" refs rewritten. Exit 0.

Upstream was already UTF-8 (zh-TW).

2. First-of-lineage: Go MudOS-v22 → FluffOS

This is the first lib in the collection from this driver. A clean compile is not a port. Architecture gaps that had to be shimmed or rewritten:

1. Go efuns in /secure/simul_efun.lpc: substr, yaml_decode / yaml_encode (JSON after convert), errorp, getenv, m_delete, member, is_web_client, is_interactive, nullp, p2p_broadcast, query_uuid / find_by_uuid, md5 (hash("md5", s)), generate_uuid, pad_str, sleep no-op, write_raw, varargs crypt, $HIW$ ANSI via write / tell_object, object_name (file_name), to_string, resolve_path. JSON encode/decode is lima’s /secure/json.lpc (do not leak its #define to_string into simul_efun). 2. nil is not a FluffOS token. site.lpc look_room and incident_d.lpc used nil; replaced with 0. That compile failure blocked 民雄 from loading. 3. connect(mixed) ignores the FluffOS port int; default lang zh-TW. set_encoding("utf-8") on login. 4. **valid_*: FluffOS passes an euid string, not the calling object. valid.lpc treats !objectp(user) as allow; string denials become 0. 5. move_object is 1-arg. Wrapper on /std/object.lpc plus the usual A->move_object(B) rewrite. 6. Inherit after globals is illegal. command_d, crafting_d, party_d, skill_d, channel_d, combat_d. 7. Mixin files that touch parent vars (combat.lpc) must be #included, not inherited. 8. save_object("/path.o") is wrong — drop the .o suffix (system_d). 9. Upstream JSON .o saves** (timeline.o, settlement state, user/wade.o) throw on FluffOS restore_object. Renamed to *.o.gojson. Wrap restore in catch. 10. global include file : <globals.h> must be set even when empty; omitting it left a stale scan_config_line buffer that made every file fail #include at line 1. 11. command_d rehash must catch(load_object) / query_verbs. A single failed cmd (cmd_help 2-arg main recurse) used to abort the hash, so look/info/quit never registered. Default fail message is 什麼?. 12. INCIDENT_D / EVENT_D / YAML_D. Several files used formosa.h macros without including it (cmd_ask, cmd_help callers, npc.lpc). incident_d called a non-existent YAML_D->parse_file; now yaml_decode(read_file(...)). 13. Web-UI JSON in logon() / setup() is catch()’d so telnet is not dropped. setup() itself is catch()’d in login so 登入成功 still prints.

3. Registration / admin

Flow: language 1/2/3 → id → password → nickname → race (1矮人 2精靈 3人類 4獸人) → nature (1靈動 2勇猛 3堅毅 4睿智) → first player names the mud and becomes god.

Live: 2 (zh-TW) → fluffos / Mud@2026 / 秦風 / race 3 / nature 2 / mud name Formosa Saga. Printed 【創世神】 god. Start: 打貓社 / 民雄鄉 (榕樹、甘蔗車、travel 到民雄車站…). info shows 秦風 level 1, 135/135 HP. quit saves and returns to the language prompt. NPC 老站長 is present after /std/npc.lpc included formosa.h.

Password input_to(..., 1) hides echo. crypt() 1-arg at create; login compares crypt(pass, saved) == saved.

4. WASM

No MySQL. Heartbeat/UI JSON is noise on telnet but not required. playable.

5. Not done this pass

A handful of cmd files still fail to compile (cmd_party mid-block decls, cmd_suicide strftime arity, cmd_record missing HMAG); rehash skips them. No outbound I3. §10.7 travel/quest/combat is below.

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

管理员 fluffos / Mud@2026 god,秦風,打貓社 / 民雄鄉。Telnet login 2 (zh-TW). Driver PID this boot 968057,cwd libs/formosasaga/work,port 40276。Live debug.log is libs/formosasaga/log/debug.log(Boot Time Sat Sep 5 12:15:02 2026) — slug-level log/ so the pre-chdir fopen succeeds. Look dumps a huge Web-UI JSON map; heartbeat cmd_info is silent on telnet (empty verb). --idle 3 after the first-login YAML load is enough.

This is not a 武侠 family/shop lib. There is no buy / list verb and no apprentice / bai. The organic stand-ins are narrative shop sites, guild join, and kill.

实测过程

1. Bank. bank 冒險者銀行, cash 0 / deposit 0. After quest gold: deposit 10000 銅幣 (1 金幣) → cash 89 金幣, bank 10000 銅幣. 1 gold = 10000 copper. Ledger math ok. 2. Shop. 民雄公有市場 市場阿嬤 ask … about 菜 → 「你要買否?」 but buy / list print 什麼?. travel pharmacy from the market reaches a stub site titled 【pharmacy】 with no 藥舖老闆 spawned. travel pharmacy from 打貓社 is not on that room's route list (找不到「pharmacy」). Conversation-only shops; do not invent a buy system. 3. Guild (拜师 stand-in). Bare guild crashed *(s)printf(): Incorrect argument to type %s because guilds/*.yaml name is a {en, zh-TW, zh-CN} mapping. After the fix: lists 魔法師集會 / 戰士盟約 / 冒險者公會. guild join adventurer → 新手冒險者. guild then shows name / rank / 貢獻. Daemon join_guild() existed; the player verb did not. 4. Quest. Bare quest crashed *No program in object '/daemon/quest_d'!quest_d.lpc failed to compile (FOOTPRINT_D / EVENT_D used without #include formosa.h), and cmd_quest loaded it *before* the empty-quest return. After the fix: quest accept old_station_master_wish (老站長的 心願), travel 民雄公有市場 already held footprint sugar_railway_minxiong, quest complete paid 150 exp + 80 金幣 + 公會貢獻 30. Reward YAML path is .c; tree is .lpc. ask 老站長 糖鐵 is narrative only (same canned line with or without the footprint). 5. Combat. kill 行腳商人 at the market: 85 HP, three rounds, 秦風 135→127. Win printed 你擊敗了 then 👻 你感覺到靈魂正在脫離肉體... 你死了 and 你在祈禱室緩緩睜開 雙眼write() in living::die() goes to this_player(). Player HP/gold unchanged (not actually dead); NPC on_death() rewards never ran. After npc::die()on_death(): cloned /std/npc 怪物, 85→0, 怪物 倒下了, +50 exp, +10 金幣 (80→90). No fake player-death line. 6. Quit / persist. quit / relogin landed back in 民雄公有市場 then later pharmacy. 冒險者公會 / 新手冒險者 / 貢獻 30 / 已完成 老站長的心願 / 80 then 89 金幣 persisted. Reward ticket id works (乘車券) but pre-fix clone still shows as 某個東西 in i (old object kept default short).

Bugs fixed this pass

1. cmds/player/cmd_guild.lpc sprintf / join. select_lang() on mapping name/desc. Added guild join <id> so join_guild() is reachable. Help updated. 2. daemon/quest_d.lpc include + .c rewards. #include "/include/formosa.h" for FOOTPRINT_D / EVENT_D. clone_reward_item() retries a .c path as .lpc. 3. cmds/player/cmd_quest.lpc. Do not load quest_d on an empty quest list; catch(load_object) if the daemon is dead; select_lang on quest name/desc; quest accept / quest complete verbs for the existing daemon API. 4. std/npc.lpc die(). Override to on_death() so combat's target->die() does not run the player prayer-room path on an NPC (fake write() death + skipped kill rewards). 5. std/object.lpc set_name. If short_name is still the default 某個東西, copy the name so reward items that only set_name() display. item/old_station_ticket.lpc also sets set_short.

Onboard leftover compile failures (cmd_party, cmd_suicide, cmd_record, cmd_rm, cmd_yamllint, cmd_incident) not in this pass. Admin save / work/data/*.gojson not committed.

6. Local run

cd libs/formosasaga
~/src/fluffos/build-debug/src/driver config.fluffos

Game port: 40276. Setup after password can take several seconds (YAML/JSON world load) — use --idle 8 with mudclient.

python3 scripts/mudclient.py 127.0.0.1 40276 --timeout 70 --idle 8 \
  --send "2" --send "fluffos" --send "Mud@2026" \
  --send "look" --send "info" --send "quit"