docs(vt): fold the citation-pass corrections into KB 07 (hn.cs line map, dh/q hierarchy, 74-function count, ReturnFromCall guard, resolved fm/a shapes)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b7ed65bc64
commit
642eacc13c
1 changed files with 33 additions and 35 deletions
|
|
@ -13,7 +13,7 @@ Cross-referenced against `docs/research/2026-07-29-vtank-plugin-automation-requi
|
|||
`docs/research/2026-08-26-mosstank-vtank-utilitybelt-research.md` (UtilityBelt's
|
||||
expression dialect). Disagreements are called out inline as **DISAGREES**. Two
|
||||
found: the 2026-07-29 doc's "67 functions" count for retail VTank (this doc's
|
||||
direct reflection-attribute count from `ch.cs` is **66** — §3.7); and the
|
||||
exact `[Expr_FunctionName]` attribute count is **74** — 17 base + 57 `ch`, §3.7; both prior figures were low, so this is "both undercounted", not a 67-vs-66 conflict); and the
|
||||
2026-08-26 doc's UtilityBelt grammar audit states `;` "return[s] the final
|
||||
result" (§3.2 there) where this doc's direct read of retail's own
|
||||
`ExpressionEvaluator.cs:787-790` shows retail **VTank** returns the *first*
|
||||
|
|
@ -45,8 +45,8 @@ at the tree checked out in worktree `eloquent-hugle-42119e`.
|
|||
| `ax` | One compiled rule: `{ az Condition, ho Action, string State }` | `ax.cs` |
|
||||
| `MultipleBase<T>` (`uTank2.Meta` namespace) | Shared container base for compound condition/action lists (`All`/`Any` conditions, `All` action) | `uTank2.Meta/MultipleBase.cs` |
|
||||
| `dj` | Shared base for the common "N named typed parameters" leaf condition/action shape | `dj.cs` |
|
||||
| `dh` | Shared base for the "zero parameters" leaf shape (serializes as a bare int `0`, not an empty table — see §1.4) | referenced by `ic.cs`, `do.cs`, `ct.cs`, `gk.cs`, `cy.cs`, `i.cs`, `co.cs`, `ao.cs` |
|
||||
| `q` | Shared UI-form base used by some scalar-int leaf types (`c1`, `gc`, `e3`, `ge`, `hy`) — same on-disk shape as a raw `gy.a(int)` scalar, distinct from `dj` | `q.cs` |
|
||||
| `dh` | Abstract `bx` base providing the parent-link/refresh plumbing; declares `i()`/`h(gy)` **abstract**, so it imposes no on-disk shape. Subclasses span every shape: zero-arg (`ct`,`do`,`gk`,`cy`,`i`,`co`,`ao`), K/V table (`ea`), `f6` blob (`ff`), scalar int (`ge`,`hy`,`gw`), and the `q`→`dj` form bases (citation pass 2026-09-06) | `dh.cs:5-14` |
|
||||
| `q` | Shared N-row UI-form base (`q : dh`); **`dj` derives from it**. Direct subclasses: `c1`, `gc`, `e3` (bare-int scalar on disk) and `fm` (hand-rolled `bd("k","v")`). `ge`/`hy` extend `dh` directly, not `q` | `q.cs:8`, `dj.cs` |
|
||||
| `bw` | The Meta-view registry: `Dictionary<string,aq>`, the 5-view cap, version gate | `bw.cs` |
|
||||
| `aq` | One created view: XML parse, size-vs-window clamp, `Dictionary<string,c0>` named-control index | `aq.cs` |
|
||||
| `c0` | Base view control (left/top/width/height/name attrs) | `c0.cs` |
|
||||
|
|
@ -70,18 +70,18 @@ with **5 columns**, all non-indexed (`"n"` flags):
|
|||
CType AType CData AData State
|
||||
```
|
||||
|
||||
— confirmed by `hn.k()` (save, `hn.cs:379-393`): `new bd("CType","AType",
|
||||
— confirmed by `hn.k()` (save, `hn.cs:339-362`): `new bd("CType","AType",
|
||||
"CData","AData","State")`, one row per rule, `cw2[0]=gy.a((int)Condition
|
||||
type)`, `cw2[1]=gy.a((int)Action type)`, `cw2[2]=Condition.i()` (the
|
||||
condition's own serialized `gy`), `cw2[3]=Action.i()`, `cw2[4]=gy.a(stateName)`.
|
||||
Load is the mirror in `hn.a()` (`hn.cs:397-431`): `d9.a((h)gy.e(cell[0]))`
|
||||
Load is the mirror in `hn.a()` (`hn.cs:364-409`): `d9.a((h)gy.e(cell[0]))`
|
||||
instantiates the condition by type id, `.h(cell[2])` deserializes it;
|
||||
same for the action from cell[1]/cell[3]; cell[4] is the state name string.
|
||||
**Any unsupported/unknown type id (including the real `h.ab` gap) fails the
|
||||
whole load** — `hn.a()` clears the entire rule table and returns false,
|
||||
logging `"Meta file load: unsupported cond/act type."` (`hn.cs:401-405`).
|
||||
logging `"Meta file load: unsupported cond/act type."` (`hn.cs:382-387`).
|
||||
A DB-format failure (missing `"CondAct"` key) does the same
|
||||
(`hn.cs:379-382` load path/`hn.cs:398`).
|
||||
(`hn.cs:371-376`).
|
||||
|
||||
### 1.2 Worked decode — one real rule from a live `.met`
|
||||
|
||||
|
|
@ -157,11 +157,11 @@ exactly 1 (`ea.cs:55`, rejects/ignores anything else on load).
|
|||
| 20 | u | Portalspace Exited | `co` | bare int 0 | — | `co.cs` |
|
||||
| 21 | v | Not | `ea` | `bd("K","V")`, **exactly 1 row** | `K`=child type id, `V`=nested gy | `ea.cs:53-62` |
|
||||
| 22 | w | Seconds in state (P) >= | `gc` | bare int | seconds, compared against a **separate, non-macro-reset clock** — see §2.5 | `gc.cs:32-46` |
|
||||
| 23 | x | Time Left On Spell >= | `fm` | `dj` table, 2 fields (inferred from serializer parity — not independently re-read; see `sid`/`sec` in §1.5) | `sid`=int spell id, `sec`=int seconds | `fm.cs` (name string only, `fm.cs:52`) |
|
||||
| 23 | x | Time Left On Spell >= | `fm` | `bd("k","v")` table, 2 rows (hand-rolled — `fm : q`, not `dj`-derived, same wart as `cp`) | `sid`=int spell id, `sec`=int seconds | `fm.cs:61-86` |
|
||||
| 24 | y | Burden Percentage (eg. 100) | `e3` | bare int | percent threshold | `e3.cs:51-65` |
|
||||
| 25 | z | Dist any route pt >= | `gf` | `dj` table, 1 field | `dist`=double meters | `gf.cs:7-12` |
|
||||
| 26 | aa | Expression | `b3` | `dj` table, 1 field | `e`=string expression text | `b3.cs:42-47` |
|
||||
| 27 | **ab** | **— unregistered —** | — | n/a | n/a | `d9.cs:9-35` — `h.ab` is never passed to `a(h,az)`; loading this id fails the whole file (§1.1) |
|
||||
| 27 | **ab** | **— unregistered —** | — | n/a | n/a | `d9.cs:14-57` (the gap itself at `d9.cs:40-41`, `h.aa`→`h.ac`) — `h.ab` is never passed to `a(h,az)`; loading this id fails the whole file (§1.1) |
|
||||
| 28 | ac | Chat Message Capture | `c5` | `dj` table, 2 fields | `p`=string regex pattern, `c`=string semicolon-joined color-id list (empty = any color) | `c5.cs:16-22` |
|
||||
|
||||
**Retail UI-name vs. semantic mismatch (a real VTank wart, not a decompiler
|
||||
|
|
@ -195,8 +195,9 @@ suggests.
|
|||
| 15 | p | Destroy All Views | `ag` | `dj` table, **0 fields** | — | `ag.cs:4-8` |
|
||||
|
||||
**Two distinct "no parameters" byte encodings exist and must not be
|
||||
conflated:** `dh`-based zero-arg types (`ic`/`a6`/`ct`/`do`/`gk`/`cy`/`i`/`co`/
|
||||
`ao`, and by analogy the `bp.a`="None" action) serialize as a **bare typed
|
||||
conflated:** the bare-cell zero-arg types (`ic`/`a6`/`ct`/`do`/`gk`/`cy`/`i`/`co`/
|
||||
`ao`, plus the confirmed `bp.a`="None" action, `a.cs:42-44`; note `ic`, `a6`
|
||||
and `a` implement `az`/`ho` directly and each spells out `gy.a(0)` itself) serialize as a **bare typed
|
||||
int cell** — two lines, `"i"` then `"0"` (`ic.cs:42-44` — `gy.a(0)` resolves
|
||||
to the `int` overload, not a true "void" `gy`). `dj`-based zero-arg types
|
||||
(`ht`/`ClearWatchdog`, `ag`/`DestroyAllViews`) instead serialize as a
|
||||
|
|
@ -242,12 +243,12 @@ fast-wake path on spell-cast/attack completion (doc 02 §1.1) — **not** a
|
|||
fixed per-frame tick, and **not** independent of the main action-list
|
||||
scheduler.
|
||||
|
||||
### 2.2 One pass, `hn.h()`, in full (`hn.cs:38-80`)
|
||||
### 2.2 One pass, `hn.h()`, in full (`hn.cs:41-92`)
|
||||
|
||||
1. If the watchdog has expired (`h7.b() == h7.a.a`, §2.6): if the call stack
|
||||
(`hn.j`, a `Stack<string>`) already holds >= 10000 entries, post
|
||||
`"Meta Error: Call stack overflow (recursive call loop?)."` +
|
||||
`"Disabling Meta."` and set `EnableMeta` false (`hn.cs:47-51`); otherwise
|
||||
`"Disabling Meta."` and set `EnableMeta` false (`hn.cs:51-56`); otherwise
|
||||
post `"Meta watchdog expired!"`, **push the current state** onto the call
|
||||
stack, and transition to the watchdog's configured target state
|
||||
(`hn.b(h7.d())`) — this reuses the SAME call/return stack as
|
||||
|
|
@ -298,7 +299,7 @@ rule comes next in list order, all in one `hn.h()` invocation.
|
|||
### 2.3 Per-state-entry firing, not per-pass firing
|
||||
|
||||
`hn.e` (`Dictionary<ax,bool>`) is the fired-set, and it is **only** cleared
|
||||
on `hn.b(string)` — a state transition (`hn.cs:104-112`) — never cleared
|
||||
on `hn.b(string)` — a state transition (`hn.cs:115-124`, `e.Clear()` at `:117`) — never cleared
|
||||
mid-state. So a rule that fires once in a state stays "spent" for every
|
||||
subsequent pass until the FSM leaves and re-enters that state (even the same
|
||||
state name via `SetMetaState "SameState"` counts as a fresh entry, since
|
||||
|
|
@ -316,8 +317,9 @@ string (`m_b`, defaulting to `"Default"` if left blank in the UI — not the
|
|||
the **call** state (`m_a`). `ReturnFromCall` (`ao.b()`, `ao.cs:73-85`) pops
|
||||
`hn.j` and transitions there; popping an empty stack posts `"Meta Error: Call
|
||||
stack underflow, cannot return."` and disables Meta. Both share the same
|
||||
10000-entry overflow guard as the watchdog-expiry path (§2.2 step 1) — **all
|
||||
three producers of stack growth (CallMetaState, watchdog expiry) and the one
|
||||
10000-entry overflow guard as the watchdog-expiry path (§2.2 step 1,
|
||||
`cp.cs:144-150`) — `CallMetaState` only; `ReturnFromCall` has NO depth guard,
|
||||
just an empty-stack underflow check (`ao.cs:75-81`). **Both producers of stack growth (CallMetaState, watchdog expiry) and the one
|
||||
consumer (ReturnFromCall) operate on the literal same `Stack<string>`
|
||||
instance** (`hn.j`), so a watchdog-triggered call can be closed by an
|
||||
ordinary `ReturnFromCall` rule and vice versa.
|
||||
|
|
@ -325,8 +327,8 @@ ordinary `ReturnFromCall` rule and vice versa.
|
|||
### 2.5 Two independent "seconds in state" clocks
|
||||
|
||||
`hn.h` and `hn.i` (both `DateTimeOffset`) are reset together on every state
|
||||
transition (`hn.b(string)`, `hn.cs:104-112`) **and** on Meta's constructor/
|
||||
full reset (`hn.g()`) — but only **`hn.h`** is additionally reset every time
|
||||
transition (`hn.b(string)`, `hn.cs:115-124`) **and** on Meta's constructor/
|
||||
full reset (`hn.c()`, `hn.cs:32-39` — not `hn.g()`, which touches neither field) — but only **`hn.h`** is additionally reset every time
|
||||
the macro is (re)started (`cLogic.StartMacro()`, `uTank2/cLogic.cs:324`,
|
||||
confirmed as the only other write site to either field via a repo-wide
|
||||
grep). `c1`/"Seconds in state >=" reads `hn.h` (`c1.cs:17`); `gc`/"Seconds in
|
||||
|
|
@ -454,7 +456,7 @@ produced/consumed by dedicated functions. A numeric literal is parsed with
|
|||
`double.TryParse(..., NumberStyles.Any, InvariantCulture)` (`:1135`) — a
|
||||
parse failure throws a bare `TokenParseError("")` (no message text).
|
||||
`getobjectinternaltype[]` exposes the raw type-id numbering used internally:
|
||||
`0`=none/uninitialized, `1`=Number, `3`=String, `7`=Object (`ch.cs:482`
|
||||
`0`=none/uninitialized, `1`=Number, `3`=String, `7`=Object (`ExpressionEvaluator.cs:482`
|
||||
description string — note the enum's actual C# ordinals are `a=0,b=1,c=2,
|
||||
d=3,...,h=7`, so the description's "3"/"7" line up with `eTokenCharType.d`
|
||||
and `.h` respectively, skipping the intermediate parser-only states `c`/`e`/
|
||||
|
|
@ -496,23 +498,26 @@ finding describe the same fact from two angles, not a disagreement.
|
|||
`eTokenCharType` has exactly the four settled kinds (Number/String/Object/
|
||||
uninitialized) enumerated in §3.4; there is no array/collection literal
|
||||
syntax, no `listcreate[]`/`dictcreate[]`-style function anywhere in `ch.cs`'s
|
||||
66-function catalog (§3.7), and no collection case in the binary-operator
|
||||
74-function catalog (§3.7), and no collection case in the binary-operator
|
||||
dispatch (`:785-931`). Any list/dict-shaped function names encountered
|
||||
belong to **UtilityBelt's** dialect, not retail VTank's — see §5.
|
||||
|
||||
### 3.7 Complete built-in function table
|
||||
|
||||
20 inherited from the base `ExpressionEvaluator<W>` plus 46 registered by the
|
||||
17 inherited from the base `ExpressionEvaluator<W>` plus 57 registered by the
|
||||
`ch` subclass (all discovered via one-time reflection over every
|
||||
`[Expr_FunctionName]`-attributed method across the class hierarchy,
|
||||
`ExpressionEvaluator.cs:306-318`) — **66 total**. Every function validates
|
||||
`ExpressionEvaluator.cs:306-318`) — **74 total** (plus the two pre-registered *variables* `true`/`false`,
|
||||
`ExpressionEvaluator.cs:341-342`, for 76 resolvable names; exact
|
||||
`[Expr_FunctionName]` attribute counts: 17 in `ExpressionEvaluator.cs`, 57 in
|
||||
`ch.cs`). Every function validates
|
||||
its own arity by comparing `A_0.Count` against the hardcoded literal in its
|
||||
body (the `[Expr_ParamCount]` attribute is metadata for the in-game function
|
||||
browser, not itself enforced at call time). "Coordinates"/"WorldObject"/
|
||||
"ViewControl" params are runtime type-checked against the boxed `.d` field's
|
||||
CLR type (`typeof(d)`/`typeof(fu)`/subclass-of `c0`).
|
||||
|
||||
**Base engine (20):**
|
||||
**Base engine (17 functions + 2 variables):**
|
||||
|
||||
| Name | Arity | Semantics | Cite |
|
||||
|---|---|---|---|
|
||||
|
|
@ -535,9 +540,8 @@ CLR type (`typeof(d)`/`typeof(fu)`/subclass-of `c0`).
|
|||
| `ceiling[n]` | 1 | `Math.Ceiling` | `:654-671` |
|
||||
| `round[n]` | 1 | `Math.Round` | `:673-690` |
|
||||
| `abs[n]` | 1 | `Math.Abs` | `:692-709` |
|
||||
| (internal `setvar` shadow) | — | Overridden by `ch`'s own `setvar` (§3.5) | — |
|
||||
|
||||
**`ch` subclass (46):**
|
||||
**`ch` subclass (57):**
|
||||
|
||||
| Name | Arity | Semantics | Cite |
|
||||
|---|---|---|---|
|
||||
|
|
@ -762,16 +766,10 @@ line counts). This is an unusually precise reverse-engineering result — the
|
|||
|
||||
## 6. Could not determine
|
||||
|
||||
- **`fm.cs` (Time Left On Spell >=, `h.x`=23)** was cited by name/type-id
|
||||
only (`fm.cs:52`, the display-name string); its field layout in §1.3 (`sid`
|
||||
int spell id, `sec` int seconds) is inferred from
|
||||
`VtankMetaProfileSerializer`'s parity with every *other* independently
|
||||
verified type in this doc, not from an independent read of `fm.cs`'s own
|
||||
`a(gy)`/`e()` methods.
|
||||
- **Action type `bp.a`="None" (`a.cs`)** was not independently re-read in
|
||||
this pass (its "bare int 0, always returns true, no-op" shape is inferred
|
||||
from its retail UI name and from `VtankMetaProfileSerializer`'s case-0
|
||||
handling, which groups it with the confirmed-`dh`-shaped `ReturnFromCall`).
|
||||
- RESOLVED (citation pass 2026-09-06): `fm.cs:61-86` hand-rolls `bd("k","v")`
|
||||
with rows `sid`/`sec` (UI labels "SpellID" / "Time Left (Seconds)",
|
||||
`fm.cs:43-44`); `a.cs:42-44` writes bare int `0`, `a.cs:32-34` loads nothing,
|
||||
`a.cs:86-89` returns `true` — the inferred shapes were right.
|
||||
- **`ChatMessageCapture`'s wiki-referenced color-id numbering** ("Chat
|
||||
colorid list", `c5.cs:19-20`) — the exact mapping from small integers to
|
||||
named chat colors was not traced here; it lives in KB's chat-color
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue