feat(chat): CT-B4 — @log, and the research block that was a wrong question
CT-B4 was filed as "the plain-text session chat log, path and rotation UNKNOWN, needs a live check." Both unknowns dissolve once you read the handler: there is no automatic session log. Retail's @log is a COMMAND. DoSetOutput @0x0057E4F0 takes a filename, StartCopyOutputToFile @0x0057C8A0 does the fopen(name, "a+"), and running it again with no argument closes it. Nothing rotates because it appends forever, and nothing has a fixed path because the player names the file. The path question that DOES exist — where a bare name lands — was answered all along by retail's own help text, which CH4 extracted verbatim into our help table a fortnight ago and nobody read: "a log file named Aclog.txt in your Asheron's Call directory." A blocked question sat on top of a committed answer. We cannot use the install directory: the launcher replaces it atomically on update, so a log written there is wiped by the next update or blocks it. The client's own log directory is the equivalent that survives. Rooted paths are honoured verbatim, as retail's fopen would. Register CT-5. The verb was registered in the help table but NOT in the command catalog, so /log printed help and did nothing — and the CH4 conformance registry recorded it as a "server passthrough" precisely because that shape is indistinguishable from an unimplemented client command. It never went on the wire at all. Both are corrected, with the totals moved in the same commit rather than left to drift. Moving it into the catalog also moves which help table answers for it, so retail's real text moved to the catalog-verb table in the same change. Without that, /help log would have silently started printing acdream's own invented one-line summary — caught by the coverage test, and now pinned by a test that names the text. All five replies are byte-decoded from the PDB-paired binary rather than read off Binary Ninja's previews, which truncate at ~33 characters and would have lost the second half of every one of them (including the two spaces retail puts after "Copying chat to %s."). The writer attaches on OPEN, not at startup — retail's help is explicit that only what appears after the command is copied — and detaches from the transcript it actually attached to, so a session teardown cannot leave a live handler writing into a file the player believes is closed. What gets written is the composed display line with the shared timestamp, because retail's fprintf sits inside AddTextToScroll: downstream of composition, upstream of glyph layout. Logging the raw entry text would have produced a file of bare fragments with no speakers. acdream's logs carry no inline tag markup where retail's do, since tags live beside the text as spans here rather than inside it. Registered as CT-6 rather than reconstructed purely to write it to a file. Register: CT-5, CT-6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
89db9a794c
commit
0e0a77c9b1
16 changed files with 831 additions and 18 deletions
|
|
@ -494,6 +494,8 @@ equivalence argument (promote to AD/AP) or a fix.
|
|||
| CT-2 | No client-side chat word filtering. Retail runs every transcript line through a taboo table when the `FilterLanguage` option is on and SUBSTITUTES matches; acdream performs no substitution at all. The option itself is kept and still stores/ships its bit to the server exactly as retail does | `src/AcDream.Core.Net/GameEventWiring.cs` (no filter in the AddText path); option at `src/AcDream.Runtime/Gameplay/CharacterOptionTable.cs` | DELIBERATE PRODUCT DECISION by the user, 2026-08-21: "I do not want any censoring." Not an oversight and not a porting gap | A player who enables FilterLanguage expecting retail's behaviour sees unfiltered text. No state, wire or server-visible effect — the option bit is still sent, so anything the SERVER gates on it behaves normally | `PlayerModule::FilterLanguage` + `TabooTableAdaptor::CheckCensorsW @0x00682A30` inside `ClientSystem::AddTextToScroll @0x00563C50`; matching at `TabooTable::CreateCheckString @0x00681570` / `StringMatchesFilter @0x00681600` |
|
||||
| CT-3 | A media `Pause` step holds for its `MinDuration`; retail authors a min AND a max and acdream ignores the max. Every sequence measured so far sets them equal, so nothing shipped is affected | `src/AcDream.App/UI/Layout/UiMediaSequence.cs` (`Sample`, the `Pause` case) | Whether the range means a random hold, a ramp, or a min-with-a-frame-budget ceiling is NOT determinable from the decomp, and picking one would be a guess dressed as a port. Using the min is the one reading that is right in every interpretation for the equal-valued case we can actually observe | A sequence authoring min != max would animate faster than retail. None does in the elements dumped so far; if one is found, the reading has to be measured before it is implemented | `MediaDescPause` in the LayoutDesc dat; playback at `UIElement::AnimateMedia` |
|
||||
| CT-4 | A media `Jump`/`State` step with a probability below 1 FALLS THROUGH rather than branching; retail rolls for it | `src/AcDream.App/UI/Layout/UiMediaSequence.cs` (`Sample`) | The roll's distribution and its re-roll cadence (per visit? per state entry?) are not in the decomp. Falling through is the conservative direction: a sequence that ends early stops animating, where treating it as certain would animate forever and could pin a state that never hands off | A probabilistic sequence plays its deterministic tail instead of its branch. The chat indicator authors p=1 throughout, so it is exact there | `MediaDescJump{Probability}` / `MediaDescState{Probability}` in the LayoutDesc dat |
|
||||
| CT-5 | A bare `@log` filename lands in the client's own log directory (`ApplicationPathSet.LogsDirectory`), not the install directory retail names ("a log file named Aclog.txt in your Asheron's Call directory"). Rooted paths are honoured verbatim, as retail's `fopen` would | `src/AcDream.App/Net/LiveSessionRuntimeFactory.cs` (`_chatLogDirectory`); `src/AcDream.Core/Chat/ChatSessionLog.cs` | acdream's launcher replaces the install directory atomically on update, so a log written there is wiped by the next update or blocks it outright. Retail had no updater with that property. The client's own data directory is the equivalent that survives | A player following retail-era instructions looks for the file next to the executable and does not find it. The `/log` reply names the file, not the directory, so the path is discoverable only from this row and the code | `ClientCommunicationSystem::StartCopyOutputToFile @0x0057C8A0`; help text at `DoSetOutputHelp @0x0057A950` |
|
||||
| CT-6 | The `@log` file records the composed line WITHOUT retail's inline text-tag markup. Retail's `fprintf` runs before glyph parsing, so its logs contain literal `<Tell:IIDString:…>` markers around tagged names | `src/AcDream.App/UI/ChatTranscriptLogWriter.cs` | acdream never puts markup in the line: `ChatVM` carries tags as SPANS beside the text (CT-A2/A3), so there is no markup at that seam to preserve. Reconstructing it purely to write it to a file would be inventing a string the client does not otherwise produce | A log diffed against a retail-era log differs on tagged lines — acdream's are the clean ones. No in-client effect | `ClientSystem::AddTextToScroll` write at `@0x00563E5B`, upstream of `UIElement_Text::InqGlyphs @0x00468EA0` |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
# Campaign CT — complete chat parity (system + GUI)
|
||||
|
||||
**Status:** Groups A, B, C and D COMPLETE 2026-08-21, each user-gated. Two
|
||||
items deliberately not shipped: CT-B3 (word filtering — dropped by user
|
||||
direction, register row CT-2) and CT-B4 (session chat log — research-blocked).
|
||||
**Status:** Groups A, B, C and D COMPLETE 2026-08-21, each user-gated.
|
||||
CT-B4 landed 2026-08-21 after the research block turned out to rest on a wrong
|
||||
premise (see the slice). One item deliberately not shipped: CT-B3 (word
|
||||
filtering — dropped by user direction, register row CT-2).
|
||||
|
||||
**Carried forward:** multi-frame state media. Retail's unseen-text indicator
|
||||
blinks because its `Normal` state authors SIX image frames; our importer keeps
|
||||
one file per state, so nothing authored can animate. The indicator is
|
||||
state-driven and correct today and gains the blink for free once that
|
||||
capability lands. It is the first known customer, not the only one.
|
||||
**Carried forward:** ~~multi-frame state media~~ **DONE 2026-08-21.** The
|
||||
importer now keeps the whole authored sequence and `UiMediaSequence` plays it.
|
||||
Measuring the real data (`LayoutDump --media 0x1000048C`) corrected the
|
||||
behaviour as well as enabling it: the indicator blinks three times over three
|
||||
seconds and then hands off to `Ghosted`, hiding itself. Retail's is a transient
|
||||
attention-flash, not a badge that stays lit until you scroll down. Register
|
||||
rows CT-3, CT-4.
|
||||
|
||||
**Goal, set by the user 2026-08-21: complete retail parity for the chat
|
||||
system AND the chat GUI.** Not "fix the green name" — that was the symptom
|
||||
|
|
@ -127,9 +130,29 @@ Nothing is user-visible until A4.
|
|||
`TabooTable::CreateCheckString @0x00681570` normalises a candidate before
|
||||
`StringMatchesFilter @0x00681600` compares it, which is how retail catches
|
||||
obfuscated spellings.
|
||||
- **CT-B4** The plain-text session chat log (`ClientSystem::s_pLogFile`). We
|
||||
write none. **Research-blocked**: path and rotation are UNKNOWN and are not
|
||||
in the decomp — needs a live check or a dat/filesystem probe.
|
||||
- **CT-B4** ~~The plain-text session chat log~~ **DONE — and the premise was
|
||||
wrong.** There is no automatic session log to have a path for. Retail's
|
||||
`@log` is a COMMAND: `ClientCommunicationSystem::DoSetOutput @0x0057E4F0`
|
||||
takes a filename, `StartCopyOutputToFile @0x0057C8A0` does the
|
||||
`fopen(name, "a+")`, and running it again with no argument closes it. So
|
||||
"path and rotation UNKNOWN" was asking a question the design does not have:
|
||||
the player names the file, and there is no rotation because it appends
|
||||
forever.
|
||||
|
||||
The path question that DOES exist — where a bare name lands — is answered by
|
||||
retail's own help text, which the CH4 help table already carried verbatim
|
||||
without anyone reading it: "a log file named Aclog.txt **in your Asheron's
|
||||
Call directory**". acdream cannot use the install directory (the launcher
|
||||
replaces it atomically on update), so a bare name lands in the client's own
|
||||
log directory. Rooted paths are honoured verbatim. Register row CT-5.
|
||||
|
||||
Landed with it: the verb registered in the catalog (it had a help entry
|
||||
since CH4 but no catalog entry, so `/log` printed help and did nothing),
|
||||
retail's `.txt`-for-extensionless rule, all five reply strings byte-decoded
|
||||
from the paired binary, and the writer attached at OPEN so only text after
|
||||
the command is copied. The line logged is the composed display line with the
|
||||
shared timestamp, because retail's `fprintf` sits inside `AddTextToScroll`
|
||||
— downstream of composition, upstream of glyph layout.
|
||||
|
||||
### Group C — chat GUI
|
||||
|
||||
|
|
@ -166,7 +189,6 @@ Nothing is user-visible until A4.
|
|||
Blocks nothing in Group A, but decides whether other tag shapes exist.
|
||||
- Whether retail's transcript supports text selection distinctly from the
|
||||
entry field (blocks CT-C4's scope).
|
||||
- The chat log file's path and rotation (blocks CT-B4).
|
||||
- Whether a chat-specific sound cue exists — a grep came back empty, which is
|
||||
weak evidence, not proof of absence.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue