v1.1.13: Main Street districts, playtest fixes, royal court and shared-record saves - #154
Merged
Merged
Conversation
A taken name now gets a surname from the immigrant and family pools, or a different surname keeping the first name; children keep the family surname with a middle surname. Trailing numerals are stripped from candidates, and the guid fragment fallback is gone. Existing names are not renamed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- Equip Best and every manual equip menu (Team Corner, Home, Inn, Dungeon) take the exact backpack item chosen, not the first of that name, so two items with one name can no longer swap stats. - The Home, Inn and Dungeon equipment menus save each gear move at once, in the order that cannot copy an item (shared-state NPCs); companions live in the player's save. Dungeon unequip of a cursed item no longer hands the player a copy. - The delete purge keeps mail sent by a living character who goes by the deleted account's name. - The king's save is found by display name, then Name2, then username, so a throne challenge and the king-level reads meet the real king rather than a stand-in. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…at once - The purge clears grudges against the name (memories recorded at or before the delete), and removes it from Enemies, KnownCharacters and negative impressions while no other player row uses the name. - An NPC married to the deleted player is divorced, with its registry entry. An NPC married to another NPC of that name keeps its marriage. - OnlineStateManager.PersistNpcWorldNow writes the roster right after the clean-up, with nothing awaited in between, under the record's version. On a conflict it reloads the stored roster, applies the clean-up again and retries. The stored marriages record loses the ended marriages the same way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- SaveRoyalCourtToWorldState writes through SaveRoyalCourtIfVersionAsync, against the stored version the in-memory court was loaded from or last written as. On a conflict it loads the stored court and skips the write. - The version is process-wide like the king: session loads, the world sim's load and save, and every versioned court write record it. - A court never loaded may write only where none is stored yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- The web delete queues an admin_commands 'delete_player' row and waits up to 20 s for its status. The game server reads the Name2, runs DeleteGameData, then runs the purge only if the delete succeeded, as the admin consoles do. - The admin poller writes mud_heartbeat. With no fresh heartbeat, or no answer in time (the command is then withdrawn), the web server deletes the rows itself and queues the purge in pending_purges. The game server runs queued purges once its world sim has loaded the roster. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…/train, choices - The world event lines say they are included in the XP gained, and one line names the other XP modifiers; the share line matches the gain. - A line typed at a pause online is kept and run next; the online pause says "Press Enter". - Auto-combat drinks a potion at or below a chosen HP share (20-70%, default 50%), set in the preferences. - Monsters on floors 4-6 are softened (0.85/0.9/0.95, a table), and a monster's stun or web on the player follows the PvP hold rules, so it cannot chain. - The combat status line shows turns left, damage per turn, turns lost to a hold, and whether Last Stand and Death's Door are ready. - /train takes you to the Level Master from Main Street or says how to get there; level 10 is announced. - Menus and room events ask again on an invalid choice instead of leaving; a room event is spent by the player's choice; the chest's Search for Traps works; room actions say why they cannot be used; the translated prompts show the keys the code accepts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…ed throne save, web admin delete) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- A restored memory keeps its saved time; restoring used to stamp every memory with the load time, so memories never aged and the 7-day decay never fired. - A roster saved before this version (no MemoryTimesKept mark) gets a one-time grace on its first load: a memory older than 7 days, or with no time, is stamped with the load time, so nothing is forgotten on the day of the update. Both serializers write the mark. - The delete's grudge clean-up uses the delete time as its cut-off on retries too, since a reload now keeps the recorded times. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- ExecuteAdminCommand moves the row from pending to executing first and runs it only if that claim changed exactly one row. A command the web server withdrew, or one an overlapping poll already claimed, is not run. - The web delete keeps waiting while its command is pending or executing. Its withdrawal still changes only a pending row; when the game server has claimed it, the web server waits up to 2 more minutes for the outcome and answers 504 if it is still running. It never deletes the rows itself after a claim. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- world_edits(id, kind, payload, created_at, created_by, applied_at, applied_by), made in the init script and again in the migrations for a database from an older release. - AppendWorldEdit; GetWorldEditsToApply (every unapplied edit, plus the last 24 h); MarkWorldEditsApplied (the first mark stands); PruneAppliedWorldEdits (applied edits after 7 days, never unapplied ones); GetUnappliedWorldEditsOlderThan for the warning line. - LaterCharacterUsesName: a player row with a save under the name created or saved after the edit, or on the deleted character's own account. - WorldSimLockOwner reads the world sim lock's owner. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- A delete appends a forget_character edit {aliases, character_key,
deleted_at, untimed} before its local clean-up and versioned write.
- The owner is the MUD process, else the process whose world sim holds the
worldsim lock. The MUD and the standalone --worldsim now take the lock and
pass their id as heartbeatOwnerId, so door sessions start no second world
sim.
- The owner applies every unapplied edit and every edit of the last 24 h
after it loads the world, and in each save after any version-triggered
reload and before the roster write. It marks an edit applied only after its
versioned write succeeds; while one is owed the write is not skipped by the
dirty-check. A process that is not the owner never marks an edit.
- Timed memories go by the delete time. Enemies, KnownCharacters,
impressions and spouses are skipped once a later character uses the name
(a player row with a save created or saved after the edit, or the same
account).
- The owner prunes applied edits after 7 days and logs a warning for any
edit unapplied after 24 h; those are never pruned.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- SaveSharedNPCs in a process that is not the owner no longer writes the npcs record unconditionally. LoadSharedNPCs records the version it read, and the online loads note a hash of each NPC as restored. - The save applies the world edits (never marking them) and writes under that version. On a conflict it loads the stored roster, lays over it each NPC this session changed since its load (an NPC the stored roster no longer has is not brought back), applies the edits again and retries under the new version. Other processes' changes to the other NPCs are kept; on an NPC both changed, the door's copy wins, as every door write did before. Each conflict is logged; after 5 it gives up with a warning and keeps the reloaded roster. - The emotional state drifts on every read, so it is left out of the hash. - The MUD process, and the door whose world sim holds the lock, are the owner and keep the unconditional write. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- Deleting a character who reigns (in this process or in the stored court)
appends a vacate_throne edit {king, aliases} before the abdication's
versioned write; the owner marks it once that write lands.
- The owner re-applies it with the other edits: the reign ends only if the
current king is still that character and no later character uses the
name, with the usual NPC succession, unpersisted.
- The world sim's court save now reports whether the stored court is its
own. A vacancy a throne edit made is written as one (the save used to skip
a court with no king), and throne edits are marked only after that
versioned write.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…name
The maintainer's choice: a colliding child keeps the family surname and
takes another first name from its own sex's pool ("Talia Holloway").
Only when every first name in that pool is taken with the surname does
it fall to two first names before the surname ("Wren Talia Holloway");
the surname stays last and the child is not renamed on load.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…y times) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
- A treasury withdrawal, deposit, loan or donation changes the player's gold only after the court write lands under the version it read; other treasury writers' unsaved changes are carried across a reload. - The immediate purge writes under the version the live roster was loaded at, and on a conflict reloads, overlays, re-applies and retries. - A purge and a roster rebuild share one lock, and a partial roster is never written; the edit log carries the clean-up instead. - The forget edit ends registry marriages by the character's ID, whatever an NPC's spouse field says. - A door save decides its own NPC changes before applying edits, keeps NPCs it created, and never revives ones another writer removed. - Queued purges keep the delete time, the untimed decision, the character ID and the married display name. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
One helper reads the stored court and its version, applies a single change to that copy, writes it under the version and retries on a conflict; the in-memory court is then the written copy. A change's cost and benefit go in the same write, and the player's side (gold, guards, pardons, bail) follows only once it lands. Treasury moves, audiences, guards, prison records, decrees, orphans, marriage, heirs, petitions, sales tax, daily activities and the world sim's tax, upkeep and court politics all use it. The carried unsaved treasury delta is gone; the periodic court save reloads on a conflict and never overwrites. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
When a delete's purge runs later (the game server was down), a players row on the key or name with a save created or used at or after the delete means the name belongs to a new character: its guild, auctions, mail, quests, succession, throne and children are left alone, and only the timed NPC memory clean-up runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Every reload records removed NPCs, so a save that conflicts again and again never brings one back, while NPCs created in this session are still kept. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
A bonus of 0 or less, above 1,000,000, or above the treasury split over the guards is refused before it is multiplied; the multiply is checked and the loyalty gain capped. A huge bonus used to overflow to a negative cost that added gold to the treasury. Donate's crown-standing gain is bounded too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Winning the throne by an NPC challenge, the sim claiming an empty throne, and a player's challenge or siege write the new court in one versioned write before anything else reads it; the flags and news follow only a landed write. The deposed king's and a failed challenger's cells are court changes too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The guards' loyalty rise is applied in the same court change as the budget it spends, so a later court change or reload keeps it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Each sale credits the crown's share at once; the daily reset no longer credits the day's takings a second time, and still reports them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
A graduating orphan is removed from the court in a versioned write before its NPC is made, and new orphans are added the same way, so a later court change can no longer bring a graduate back to graduate again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The guards and monsters a challenger beat, the missed-defence loyalty penalty and the desertions are written with the challenger's cell, so the cell's write no longer restores them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…red at once Each writes the new court (or a marked vacancy) in one versioned write; the player's flags, marriage clean-up, gold and news follow only a landed write, and a failed one tells the player and changes nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The loaders apply the default rates only when a rate is missing from the stored court, never to a stored 0, online and single-player. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
A courtier is matched by position, name, role and faction, never by name alone, so namesakes are no longer both dismissed or the wrong one promoted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The bail is set on the stored prison record in a court change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
A pardon removes the court record when there is one; an ordinary arrest without one is released and nothing is written. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The advisor appointment, the guard loyalty penalty and a dethroning in the street throne challenge are written in court changes. The encounter has no caller today. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The served-sentence release and the single-player guard upkeep use court changes; unused King mutators are gone; a comment-stripped check over every court member and entry field, with no allowlist, finds none left. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…t once Ending a reign by ascending at the Pantheon or through an ending writes the successor's court (or a marked vacancy) in one versioned write, and the player's flags, marriage clean-up and news follow only a landed write. The castle's fallback court for a player king is written only when no court is stored; otherwise the stored court is loaded. The source check now also flags a whole-king replacement outside the named loaders. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
The first visit after a menu tier rises names what just opened; an existing character is not told about places it already had. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
You may retreat or leave; you cannot go deeper until that god is defeated or won over. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
A tank favours defence, health and strength, a caster wisdom, intelligence and mana, a healer wisdom and mana, a damage dealer its attack stats; an item with nothing useful never beats one that has. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Main Street shows its places, then Merchant Row, Guild Row, Home & Hearth, Castle Grounds, Status, the Notice Board and, online only, the Online hub, each opening a short screen of its places. Every screen shows only its unlocked keys and every key does what it shows; the old hidden keys are gone and R returns from each screen. The unlock levels are unchanged, a district appears once anything in it opens, and the unlock notice names the district. News is one entry, [?] opens a map of Main Street, and an existing character is told once that the street was reorganised. The previous layout is kept at tag mainstreet-classic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Every text that pointed at an old Main Street key now names the district and key, for example Guild Row [G], Level Master [V]. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
…atus sheet Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CT1VB81uLs5VNysGendLKY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Version 1.1.13. Release notes:
DOCS/release-notes/RELEASE_NOTES_1.1.13.md; Steam text:DOCS/release-notes/steam/STEAM_RELEASE_NOTES_1.1.13.txt.Changes
MainStreetDistricts.cs). Each screen shows only its unlocked keys; the legacy hidden keys and 88 dead Loc keys per language are removed; the level tiers are unchanged; unlock announcements name the district; Main Street's [?] opens a place map. The previous layout is kept at tagmainstreet-classic(4c50c11)./trainand a level-10 notice;admin_commandswith a claim;world_editslog re-applied by the owner process; door roster saves under the loaded version, with overlay, tombstones and retry.OnlineStateManager.ApplyCourtChangeAsync,CrownAsync), with cost and effect in one write and the player's side after it lands. A comment-stripped source check fails on any court member change, or whole-king replacement, outside it.Deploy note
The first start creates the
world_edits,pending_purgesandmud_heartbeattables and adds thepending_purgescolumns; no manual database step. Self-hosted setups with one process per player must run player processes with--no-worldsimbeside one--worldsimprocess.Listed for 1.1.14
NPCPetitionSystem~1154).CastleLocation~8581).StreetEncounterSystem~3041).FactionSystem~360).Danger: :on the dungeon screen, room text wrapping mid-word.Tests
1,995 passing, up from 1,731.