# Legacy Recon 01 — Global Variables & Constants Catalog

Reverse-engineered from extracted GML in `extracted/client-5-8` (BN Online Client 5-8)
and `extracted/server-5-4` (BN Online Master 5-4). Plain-text GML only — no hex.

All file:line references are relative to `extracted/`. Client = `client-5-8`,
Server = `server-5-4`. Line numbers are 1-based as shown by the Read tool.

> **Headline:** BNO has **no dedicated "owns expansion X" boolean**. Expansion
> ownership, zone/area unlocks, friends, spectrum cosmetics, and message data are
> ALL stored in one unified inventory structure, `global.iv_itemdb[category, idx]`,
> a 2-D array of strings. "Owns X" = "a matching string is present in the right
> category". See the **Inventory / Data Model** section — it is the single most
> load-bearing finding in this doc.

---

## 1. Inventory / Data Model — the master state container

### `global.iv_itemdb[cat, idx]` — the unified inventory (HIGH PRIORITY)

The whole "what does this player own" state lives here. It is a sparse 2-D array
of strings. `[cat,0]` = item **count** in that category; `[cat,1..n]` = item
**payload strings**.

| Category index | Meaning | Payload string format | Source |
|---|---|---|---|
| 0 | (reserved / "All" tab — initialized to 0) | — | Client `objects/0034-title/events/Other-2.gml:209-212` |
| 1 | **Key data** (Navi Expansions + Mod items) | the *keydata index* as a string, e.g. `"2"`, `"909"` | Client `scripts/0302-iv_datagather.gml:6-12` |
| 2 | **Friend data** | `"<uid>|<name>"`, e.g. `"7|SaberMage"` | Client `scripts/0302-iv_datagather.gml:14-30` |
| 3 | **Link data** (zone + area unlocks) | `"<isZone>\|1]<name>\|2]<dest>\|3]<zone#>\|4]<x>\|5]<y>"` | Client `scripts/0302-iv_datagather.gml:31-39`, `0327-iv_ldatainfo.gml` |
| 4 | **Message data** | `"...\|1]title\|2]author\|..."` (parsed by `iv_mdatainfo`) | Client `scripts/0302-iv_datagather.gml:40-54` |
| 5 | **Spectrum data** (cosmetics) | `"<part#>\|<colorIdx>"`, e.g. `"1|3"` | Client `scripts/0302-iv_datagather.gml:55-83` |

`global.iv_totalcats = 6` (= 2 + 4 collective categories). Initialized at
Client `objects/0034-title/events/Other-2.gml:207`.

**Ownership test pattern** — `iv_itemsearch(category, matchString)` linearly scans
`iv_itemdb[category, 1..count]` and returns the found index (≥1) or `0`:
```
// Client scripts/0320-iv_itemsearch.gml:5-9
for(iv = 1; iv <= global.iv_itemdb[argument0,0]; iv += 1)
  if(string(global.iv_itemdb[argument0,iv]) == argument1) return iv;
return 0;
```
**So "player owns expansion X" ⟺ `iv_itemsearch(1, "<X's keydata index>") > 0`.**

**Adding an item** — `iv_additem(cat, str)` appends and immediately re-uploads the
whole inventory to the server (`iv_updateserver()`):
```
// Client scripts/0323-iv_additem.gml
global.iv_itemdb[argument0,0] += 1;
global.iv_itemdb[argument0,global.iv_itemdb[argument0,0]] = argument1;
iv_formattable(iv_tabcol2db());
iv_updateserver();
```

### Supporting inventory globals

| Global | Meaning | Evidence |
|---|---|---|
| `global.iv_totalcats` | total categories (=6) | Client `0034-title/.../Other-2.gml:207` |
| `global.iv_coltypes[]` | collective-tab labels: [0]=4, 1=Friend, 2=Link, 3=Message, 4=Spectrum | Client `objects/0393-iv_backdraw/events/Create.gml:147-151` |
| `global.iv_coltype` | currently selected collective sub-tab (0..3) | Client `scripts/0299-iv_infochange.gml:23-35` |
| `global.iv_tabindex` | top-level inventory tab; 0 = collective tabs | Client `scripts/0304-iv_tabcol2db.gml` |
| `global.iv_actions[row,col]` | per-item action menu (label/desc/exec-string) rebuilt on select | Client `scripts/0347-iv_keyactions.gml`, `0308-iv_setactions.gml` |
| `global.iv_itemtable[y,x]` | formatted on-screen grid mapping to db strings | Client `0308-iv_setactions.gml`, `0393-iv_backdraw` |
| `global.iv_curdata` | currently selected raw data string | Client `0308-iv_setactions.gml:109` |
| `global.iv_sely / iv_selx / iv_selxmax / iv_selymax / iv_toprow / iv_sellevel` | inventory cursor/scroll state | Client `0393-iv_backdraw` keypress events |
| `global.iv_icname / iv_icinfo / iv_icsprite / iv_icid / iv_selicon / iv_info / iv_preview` | rendered detail for selected item | Client `0302-iv_datagather.gml`, `0299-iv_infochange.gml` |
| `global.iv_itemtable, iv_endrow, iv_iconmax, iv_iconxpad, iv_iconypad, iv_infomaxlength` | layout tunables | Client `0393-iv_backdraw/events/Create.gml` |

### Inventory wire & save formats

- **Upload (client→server):** packet `writebyte(8)` then `iv_totalcats`, then per
  category `writeint(count)` + `writestring` per item — Client
  `scripts/0324-iv_updateserver.gml`.
- **Download (server→client):** inside login packet, mirror loop —
  Client `scripts/0094-begin_client_receive.gml:69-77`.
- **Server persistence:** `UserData\Inv\Inventory_<uid>.bnu` (text), preceded by a
  `dabypass` flag and `iv_totalcats`, then count+strings per category —
  Server `scripts/0376-uinv_backup.gml`, restore `0377-uinv_restore.gml`.
- Server stores per-user inventories as **dynamically-named globals**
  `global.u<uid>inv_itemdb[cat,idx]`, written via `execute_string` —
  Server `scripts/0374-uinv_set.gml`, `0375-uinv_get.gml`.

---

## 2. Navi Expansions — the `*Plugn` family (HIGH PRIORITY)

Expansions are **Key data items** (category 1). The catalog lives in
`global.keydata[idx, field]` where field `0`=display name, `1`=short code,
`2`=description. Defined once at Client `objects/0034-title/events/Other-2.gml:99-180`.

| keydata idx | Code | Name | Meaning / gate | Evidence |
|---|---|---|---|---|
| **1** | `HxpPlugn` | Hexport Plugin | enables hexporting / hexporters | `Other-2.gml:100-102` |
| **2** | `RmtPlugn` | Remote Plugin | enables remote teleport via link data | `Other-2.gml:105-107` |
| **3** | `MskPlugn` | Mask Plugin | enables spectrum/mask (cosmetic) compatibility | `Other-2.gml:110-112` |
| **4** | `TakABrak` | Take-A-Break.exe | easter-egg; runs a bundled exe | `Other-2.gml:115-117` |
| **5** | `DuoPlugn` | Duo Plugin | enables the two-navi duo system | `Other-2.gml:120-122` |
| **6** | `WrpPlugn` | Warp Plugin | in-zone warping via local teleports | `Other-2.gml:125-127` |
| 900–909 | `<M:...>` | Mod operation data | moderator/RCE tools (Executor, Query, NetExc, Loc, JkrShl…) | `Other-2.gml:133-180` |

> **Answer to "are there other `*Plugn` not in the brief's list?"** — Yes, one:
> **#3 `MskPlugn` (Mask Plugin)** was NOT in the mission's list (the brief mapped
> "MskPlugn=spectrum/mask" but used the wrong code; the actual mask plugin is
> #3 and it is what gates spectrum cosmetics). Also note the brief's
> `RmtPlugn`/`WrpPlugn` semantics are confirmed: **Rmt=#2 remote (link-data
> teleport), Wrp=#6 in-zone warp.** No other `*Plugn` codes exist in `keydata`.
> (Items 900–909 are mod data, not expansions.)

### How ownership gates behavior (confirmed examples)

| Gate | Check | Evidence |
|---|---|---|
| Remote teleport via link data | `iv_itemsearch(1,'2')` (RmtPlugn) | Client `0308-iv_setactions.gml:109-110,138` |
| Spectrum cosmetics allowed | `iv_itemsearch(1,"3")` (MskPlugn) — if absent, colors reset to defaults | Client `0094-begin_client_receive.gml:87-94` |
| Duo eligibility (`global.canduo`) | `iv_itemsearch(1,"5") > 0` (DuoPlugn) → sets canduo, reported to server | Client `0094-begin_client_receive.gml:96-103` |
| Joker Shell (mod) | `iv_itemsearch(1,"909") > 0` | Client `objects/0000-server/events/KeyPress-74.gml:2` |

There is **no per-expansion boolean global**; the inventory IS the source of truth.

---

## 3. Link Data inventory — zone/area unlocks (HIGH PRIORITY)

Link data = category 3 of `iv_itemdb`. Each item is a `|`-delimited string parsed by
`iv_ldatainfo(field, string)` — Client `scripts/0327-iv_ldatainfo.gml`:

| field | meaning |
|---|---|
| 0 | **isZone** flag (1 = zone link data, 0 = area link data) |
| 1 | data name |
| 2 | destination name |
| 3 | zone number |
| 4 | dest X (area data only) |
| 5 | dest Y (area data only) |

- **"Has player unlocked zone N?"** ⟺ `iv_zonesearch(N) > 0`, which scans cat-3 for
  an item where `isZone==1 && zone#==N` — Client `scripts/0330-iv_zonesearch.gml`.
- **"Has player unlocked a named link/area?"** ⟺ `iv_linksearch(name)` matches by
  data name — Client `scripts/0329-iv_linksearch.gml`.
- Teleport eligibility combines both: you need RmtPlugn AND the destination zone's
  main link data AND the current zone's link data (or a portal) — Client
  `0308-iv_setactions.gml:109-156`.
- **Home location** (`global.homedata`) is just an *index into cat-3 link data*
  (or `-1` = Online Command Screen). Validated on login against zone unlocks —
  Client `0094-begin_client_receive.gml:104-110`, set/reset in `0308-iv_setactions.gml:157-202`.

---

## 4. Spectrum Data (cosmetics) ownership/equip (HIGH PRIORITY)

- **Ownership:** category 5 of `iv_itemdb`; payload `"<part#>|<colorIdx>"` —
  Client `0302-iv_datagather.gml:55-83`. Parts 1–5 = helmet/body, gloves/boots,
  trim, stripes, core circles.
- **Available color palette** per part: `global.ncol_options[part, slot]` with
  `[0,part]` = count — Client `0034-title/events/Other-2.gml:49-89`.
- **Equipped colors:** `global.ncol[part, ...]` (current equip), applied via
  `set_navi_colors(part, letter)` — Client `0034-title/events/Other-2.gml:39-45`,
  used throughout. Equip validity is gated on owning each color in cat-5 AND
  owning MskPlugn (#3) — Client `0094-begin_client_receive.gml:82-94`.
- Peer cosmetics are cached in `global.us<N>col[...]` arrays (per spectrum set 1–5)
  built by `set_user_colors` — Client `scripts/0322-set_user_colors.gml`.

---

## 5. Friend Data (HIGH PRIORITY)

- **Storage:** category 2 of `iv_itemdb`; payload `"<uid>|<name>"` —
  Client `0302-iv_datagather.gml:14-30`. uid `99` is the sentinel "Add another
  player's friend data" placeholder entry.
- Online/offline status looked up via `global.p_online[global.u_pid[uid]]` —
  Client `0302-iv_datagather.gml:22`.
- Actions (whisper/join/summon/delete) built in `0308-iv_setactions.gml:10-87`.

---

## 6. Home Portal / Fall handling / Digital Abyss (HIGH PRIORITY)

| Global | Meaning | Evidence |
|---|---|---|
| `global.homedata` | index into cat-3 link data used as home spawn anchor; `-1` = OCS | Client `0045-commandob/events/Create.gml:25-44`, `0308-iv_setactions.gml` |
| `global.gonehome` | flag: home-transfer already performed this session | Client `0045-commandob/events/Create.gml:42-44`, `Alarm.gml:2` |
| `global.dafalls` | running count of falls; ≤5 falls are "safe" (respawn in place), then Abyss roll | Client `objects/0000-server/events/Step.gml:169,173,187` |
| `global.dabypass` | per-player flag: bypass Digital Abyss on fatal fall (sent from server) | Client `0094-begin_client_receive.gml:65`; Server stores per-uid `global.dabypass[uid]` in `.bnu` (`0376-uinv_backup.gml:4`) |

**Fall logic** — Client `objects/0000-server/events/Step.gml:165-205`:
- On falling off an edge (`tcollide == 0`), `global.dafalls += 1`.
- `if(ceil(rand) > 9 || global.dafalls <= 5)` → respawn at current area (safe).
- Else → sent to **`"Abyssal Ruin"` / `Digital_Abyss` room**, `dafalls` reset to 0.
- Duo exception: a digger's fatal fall instead joins the spotter ("Duo Net",
  packet `writebyte(23)`) — `Step.gml:182-201`.
- `ac_dfalls` collision → `change_area("Divine Falls")` — Client
  `objects/0446-ac_dfalls/events/Collision-0.gml`.
- Digital Abyss / Abyssal objects: `0123-ac_abyssalpathway`, `0388-ac_abyssalruin`,
  `0544-ac_abyssaleclipse`, `0387-todigitalabyss`, room `0049-Digital_Abyss`.

---

## 7. Movement / Tile / Camera tunables (HIGH PRIORITY)

| Constant / var | Value | Meaning | Evidence |
|---|---|---|---|
| `global.curspeed` | 3 (default), 5 (toggle), 7 (joker shell) | peak run velocity (px/step) fed to `fspeed` | Client `0000-server/events/Create.gml:23`, `KeyPress-45.gml:5-12`, `KeyPress-74.gml:13`, `Keyboard-39.gml:17` |
| `fspeed` | = `global.curspeed` while running, 0 standing | instantaneous free-move speed (instance var) | Client `0000-server/events/Keyboard-37..40.gml`, `Step.gml:102-105` |
| Hexport accel | `fspeed += 0.2` up to cap **8** | hexport ramp & peak | Client `0000-server/events/Step.gml:28-35` |
| Mover-tile accel | `+2` per step while on mtile, `-1` decay, clamped **0..6** | conveyor/"mover" tiles (`mtile1r/l/u/d`, `mtile3*`) push `s{r,l,u,d}speed` | Client `0000-server/events/Step.gml:54-100` |
| Ice / slippery | `ispeed` carries momentum on `icetile`; stops on `tile1`/`icepillar`/door | slippery-ice behavior; direction snapped to nearest 90° | Client `0000-server/events/Step.gml:107-153` |
| `flicker` | toggles 0/1 every step | per-step flicker timing flag (used for invuln/spawn flicker) | Client `0000-server/events/Step.gml:218-219` |
| Floor tile | **44 × 40 px** | canonical tile pitch (see CLAUDE.md / `Tile1` meta) | (project constant, not re-derived here) |
| Navi collision box | x+9..x+26, y+39..y+47 (≈17 px wide) | mover/border collision rectangle | Client `0000-server/events/Step.gml:54,223-243` |
| Room speed | **30 steps/sec** | uniform across ALL 16 rooms | every `rooms/*/meta.json` `"speed":30` |
| View / camera | **640 × 480** | primary view viewport | `rooms/0058-BNCentral/meta.json` `views[0]` |
| BNCentral room | 8000 × 6400 | room dims | `rooms/0058-BNCentral/meta.json` |
| `DIR_UR / DIR_DR / DIR_UL / DIR_DL` | 45 / 315 / 135 / 225 (inferred) | diagonal GM math directions | used in Client `0000-server/events/Keyboard-37..40.gml`; **defs not in extracted GML** (GM 5.3a game-settings constants) |

---

## 8. Other notable globals by subsystem

### Identity / session (client)
| Global | Meaning | Evidence |
|---|---|---|
| `global.playername` | local player's navi name | Client `0034-title/.../Other-2.gml:29`, `0318-load_settings.gml:6` |
| `global.password` | local stored password (plaintext in `.bno`) | Client `0318-load_settings.gml:8` ⚠ |
| `global.clientver` | client version (=58) | `0034-title/.../Other-2.gml:11` |
| `global.latestclient` | server-reported latest client ver | `0094-begin_client_receive.gml:66` |
| `global.serverip` | server address (default `127.0.0.1`) | `0034-title/.../Other-2.gml:30` |
| `global.clientid` | assigned client id | `0094-begin_client_receive.gml` region |
| `global.jid` | join-id / spawn-source index (-1 none, 99 fall, 100 teleport) | `0000-server/events/Create.gml:4-17`, `Step.gml:170` |

### Networking / sockets (both)
| Global | Meaning | Evidence |
|---|---|---|
| `global._39dll` | handle to the 39dll networking DLL | both trees, `dllinit` |
| `global.s_tcpsocket` / `s_udpsocket` / `s_udpport` | primary server sockets | Client `0024-iv_updateserver` etc. |
| `global.loc_udpsocket` / `loc_udpport` | local/area UDP socket | Client `0094-begin_client_receive.gml:60-64` |
| `global.p_tcpsocket / p_udpsocket / p_udpport` (server) | per-player sockets | Server `objects/*`, `init_user` |
| `global.tcprec / tcpsend` | recorded TCP throughput counters | Client `0318-load_settings.gml:31-33` |

### Peer/player table (client mirror of all visible navis)
Arrays indexed by player slot `0..global.pindex`: `global.p_x[]`, `p_y[]`,
`p_xdif/p_ydif`, `p_name[]`, `p_room[]`, `p_area[]`, `p_spr[]`, `p_online[]`,
`p_uid[]`, `p_watchable[]`, `p_canduo[]`, `p_induo[]`, `p_created[]`, `p_obid[]`,
`p_fps`. Indices 99 (fall) and 100 (teleport) are reserved sentinels. Evidence:
Client `0000-server/events/Step.gml:253-262`, `Create.gml:9-15`,
`0045-commandob/events/Create.gml:28-34`.

### Duo system
`global.duopid` (partner id, -1 none), `global.duorole` (spotter/digger),
`global.canduo` (eligibility from DuoPlugn), `global.watching`/`watchpid`/
`watchable`/`watx`/`waty` (spectate). Evidence: Client `0000-server/events/Step.gml:175-271`,
`0094-begin_client_receive.gml:96-103`, `0347-iv_keyactions.gml:78-99`.

### Room / area / teleport positioning
`global.area` (current area name string), `global.parea`/`prearea` (prior area),
`global.zone` (numeric zone id), `global.posenter` (flag: position-on-enter),
`global.telepoint` (entry telepoint index), `global.teleportx[]`/`teleporty[]`
(per-telepoint coords). Evidence: Client `rooms/0058-BNCentral/creation-code.gml`,
`rooms/0048-Emptiness_Hull/creation-code.gml`.

### Message board (offline + online)
`global.mb_level` (0 topics / 1 replies / 2 reading), `mb_cboard/ctopic/creply`,
`mb_total[]`, `mb_board[]`, `mb_topic`, `mb_reply`, `mb_newmsg[]`, `mbmaxlength=74`,
`mblheight=17`, `mbttopline=-1`. Init at Client `0034-title/.../Other-2.gml:188-203`.
Server persists boards to `.bnu` via `mb_backup`/`mb_restore`.

### Chat / line buffer
`global.line[]`, `global.color[]`, `global.maxline`/`cmaxline`, `global.bottomline`,
`global.cline`, `global.showline`/`showarg`, `global.notif`/`notifon`,
`global.alphaon`, `global.bkmbon`. Client `scripts/0000-script_initlines.gml`,
`0001-addline.gml`, `0318-load_settings.gml`.

### Color constants (chat semantics)
`global.c_server=c_silver`, `c_whisper=c_aqua`, `c_request=c_fuchsia`,
`c_getdata=c_lime`, `c_ninja=rgb(40,40,40)`. Client `0034-title/.../Other-2.gml:16-20`.

### Server-only globals
`global.u_total` (user count), `global.u_name[]`/`u_pwd[]` (from `localList.txt` ⚠
plaintext), `global.u_pid[]`/`u_uid`/`u_target`/`u_beento[uid,area]` (per-user area
visited flags), `global.totalareas`, `global.maintmode` (maintenance flag),
`global.mod_uid`, `global.online`, `global.lltime`, `global.debug`, port vars
(`mainport`, `mainudpport`, `playerudpport`). Evidence: Server `scripts/0392-users_load.gml`,
`0369-uarea_backup.gml`, `0360-init_user.gml`.

---

## 9. Save / persistence file formats (call-order-derived)

| File | Owner | Contents (in order) | Evidence |
|---|---|---|---|
| `BNO Data\Settings.bno` | client | playername, password⚠, alphaon, bkmbon, serverip, [5×navi colors], notifon, homedata(+1), tcprec, tcpsend | Client `0318-load_settings.gml`, `0316-save_settings.gml` |
| `UserData\Inv\Inventory_<uid>.bnu` | server | dabypass, iv_totalcats, then per-cat: count + N strings | Server `0376-uinv_backup.gml`, `0377-uinv_restore.gml` |
| `UserData\Areas\Areas_<uid>.bnu` | server | per-area `u_beento` flag × totalareas | Server `0369-uarea_backup.gml` |
| `UserData\HXB\Bridges_<uid>.bnu` | server | hexport-bridge count + N strings (`global.hxbridge`) | Server `0384-uhxb_backup.gml`, `0383-uhxb_restore.gml` |
| `localList.txt` | server | alternating name / **plaintext password** lines ⚠ | Server `0392-users_load.gml` |
| MB/news `.bnu` | server | message board + per-user news backups | Server `0365-mb_backup.gml`, `0371-unews_backup.gml` |

> ⚠ `.bno` is the **client** settings file; `.bnu` files are the **server-side**
> per-user persistence. The mission brief's `.bnb` extension was not observed in
> 5-8/5-4 GML — only `.bno` and `.bnu` appear. (`.bnb` may be an older revision;
> flagged as open question.)

---

## 10. Confidence / open questions

**High confidence (direct, multi-site evidence):**
- Inventory unified model (`iv_itemdb[cat,idx]`), category meanings, and the
  `iv_itemsearch`-based ownership test. This is the backbone of all "ownership".
- The six `*Plugn` expansions, their keydata indices, and the specific gates
  (`'2'` remote, `'3'` mask/spectrum, `'5'` duo, `'909'` joker).
- Link data format & `iv_zonesearch`/`iv_linksearch`/`iv_ldatainfo` semantics.
- Room speed 30, view 640×480, curspeed tiers 3/5/7, hexport cap 8, mover clamp 0..6.
- Save file layouts for `.bno` and the `.bnu` family (call-order derived).

**Medium / inferred:**
- `DIR_UR/DR/UL/DL` numeric values (45/315/135/225) — standard GM math-direction
  convention; the macro **definitions are not present** in extracted per-resource
  GML (GM 5.3a keeps game-info constants in the settings block, which the extractor
  emitted as `settings.json` without a constants array). **Open: confirm by locating
  the constants table.**
- `global.dabypass` exact semantics: it is per-uid on the server (`dabypass[uid]`)
  but read as a scalar on the client (`readbyte()`). Likely "this player skips the
  Abyss on fatal fall". Confidence medium.
- `flicker` is a bare 0/1 toggle each step; its consumer (draw alpha / invuln
  window) wasn't traced here — **open: find where `flicker` is read.**
- "Flicker present/gone timing" from the brief: only the per-step `flicker` toggle
  was found; no separate present/gone duration constants surfaced. **Open question.**

**Open questions / not yet found:**
- `.bnb` file format — not referenced in 5-8/5-4 GML (only `.bno`/`.bnu`). May be a
  legacy revision artifact.
- Exact `mtile3*` vs `mtile1*` distinction (both add +2 to mover speed; likely
  different tile graphics / one-way variants).
- Server-side authoritative validation of inventory (server appears to trust the
  client's full `iv_updateserver` upload via packet 8 — relevant to REBNO's
  server-authoritative hard rule; flag for the rebuild).

---

*Generated by legacy-recon pass 01. Evidence-based; inferences are flagged inline.*
