mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-23 16:13:04 +01:00
Most of the Tower/Tenrouirai system is already implemented on develop. This doc captures what works, what is broken (PostTenrouirai Op=1 no-op, block2 never written, PresentBox empty), and the remaining unknown packet fields — so future contributors know exactly where to focus effort. Also updates unimplemented.md to note the feature/tower branch was superseded by direct integration into develop.
512 lines
21 KiB
Markdown
512 lines
21 KiB
Markdown
# Tower / Sky Corridor (天廊 / Tenrou)
|
||
|
||
Tracks what is known about the Tower system and what remains to be reverse-engineered
|
||
or implemented in Erupe.
|
||
|
||
The core of this system **is already implemented on `develop`** via the repository and
|
||
service pattern (`repo_tower.go`, `svc_tower.go`, `handlers_tower.go`). Two branches carry
|
||
earlier work: `wip/tower` predates the refactor and uses direct SQL; `feature/tower` merges
|
||
`wip/tower` with `feature/earth`. Their useful findings are incorporated below.
|
||
|
||
The `feature/tower` branch is **not mergeable in its current state** — it diverged too far
|
||
from `develop` and was superseded by the direct integration of tower code into `develop`.
|
||
Its main remaining value is the `PresentBox` handler logic and the `TimeTaken`/`CID`
|
||
field naming for `MsgMhfPostTowerInfo`.
|
||
|
||
---
|
||
|
||
## Game Context
|
||
|
||
The **Sky Corridor** (天廊, *Tenrou*) is a permanent dungeon introduced in MHFG6. Players
|
||
explore a multi-floor structure built by an ancient civilization on a desolate island. Unlike
|
||
quest zones, the Sky Corridor has its own progression systems independent of normal quests.
|
||
|
||
### Individual Tower Progression
|
||
|
||
- Each clear adds **10 minutes** to the timer; quests consist of 2–4 randomly selected floors.
|
||
- Each floor contains 2–3 rooms separated by gates, with Felynes that drop **purple medals**,
|
||
fixed-position treasure chests, and shiny crystals.
|
||
- Players accumulate **Tower Rank (TR)**, **Tower Rank Points (TRP)**, and
|
||
**Tower Skill Points (TSP)**.
|
||
- TSP is spent to level up one of 64 tower-specific skills stored server-side.
|
||
- Two floor-count columns exist in the DB (`block1`, `block2`), corresponding to the
|
||
pre-G7 and G7+ floor tiers respectively.
|
||
|
||
### Duremudira (The Guardian)
|
||
|
||
**Duremudira** (天廊の番人, Tower Guardian) is the Emperor Ice Dragon — an Elder Dragon
|
||
introduced in MHFG6. It appears as an optional boss in the second district of the Sky
|
||
Corridor. Slaying it yields **Red and Grey Liquids** used to craft Sky Corridor Gems and
|
||
Sigils.
|
||
|
||
**Arrogant Duremudira** is a harder variant; less information about it is available in
|
||
English sources.
|
||
|
||
### Gems (Sky Corridor Decorations)
|
||
|
||
Gems are collectibles (30 slots, organized as 6 tiers × 5 per tier) obtained from the Tower.
|
||
They slot into equipment to activate skills without consuming armor skill slots. Stored
|
||
server-side as a 30-element CSV in `tower.gems`.
|
||
|
||
### Tenrouirai (天廊威来) — Guild Mission System
|
||
|
||
A guild-parallel challenge system layered over the Sky Corridor. Each guild has a
|
||
**mission page** (1-based) containing 3 active missions. Guild members contribute scores
|
||
by completing tower runs; when cumulative scores meet all three mission goals, the page
|
||
advances (after a guild RP donation threshold is also met). There are 33 hardcoded
|
||
missions across 3 blocks.
|
||
|
||
**Mission types** (the `Mission` field in `TenrouiraiData`):
|
||
| Value | Objective |
|
||
|-------|-----------|
|
||
| 1 | Floors climbed |
|
||
| 2 | Antiques collected |
|
||
| 3 | Chests opened |
|
||
| 4 | Felynes (cats) saved |
|
||
| 5 | TRP acquisition |
|
||
| 6 | Monster slays |
|
||
|
||
### Relation to the Earth Event Cycle
|
||
|
||
The Tower phase is **week 3** of the three-week Earth event rotation (see `docs/conquest-war.md`).
|
||
The `GetWeeklySeibatuRankingReward` handler (Operation=5, IDs 260001 and 260003) already
|
||
handles Tower dure kill rewards and the 155-entry floor reward table. These do not need to be
|
||
reimplemented here.
|
||
|
||
---
|
||
|
||
## Database Schema
|
||
|
||
All tower tables are defined in `server/migrations/sql/0001_init.sql`.
|
||
|
||
### `tower` — per-character progression
|
||
|
||
```sql
|
||
CREATE TABLE public.tower (
|
||
char_id integer,
|
||
tr integer, -- Tower Rank
|
||
trp integer, -- Tower Rank Points
|
||
tsp integer, -- Tower Skill Points
|
||
block1 integer, -- Floor count, era 1 (pre-G7)
|
||
block2 integer, -- Floor count, era 2 (G7+)
|
||
skills text, -- CSV of 64 skill levels
|
||
gems text -- CSV of 30 gem quantities (6 tiers × 5)
|
||
);
|
||
```
|
||
|
||
`skills` and `gems` default to `EmptyTowerCSV(N)` — comma-separated zeros — when NULL.
|
||
Gems are encoded as `tier << 8 | (index_within_tier + 1)` in wire responses.
|
||
|
||
### Guild columns (`guilds` and `guild_characters`)
|
||
|
||
```sql
|
||
-- guilds
|
||
tower_mission_page integer DEFAULT 1 -- Current Tenrouirai mission page
|
||
tower_rp integer DEFAULT 0 -- Accumulated guild tower RP
|
||
|
||
-- guild_characters
|
||
tower_mission_1 integer -- Member's score for mission slot 1
|
||
tower_mission_2 integer -- Member's score for mission slot 2
|
||
tower_mission_3 integer -- Member's score for mission slot 3
|
||
```
|
||
|
||
---
|
||
|
||
## Packet Overview
|
||
|
||
Ten packets implement the Tower system. All live in `network/mhfpacket/`. None have
|
||
`Build()` implemented (all return `NOT IMPLEMENTED`).
|
||
|
||
### `MsgMhfGetTowerInfo` — Client → Server → Client
|
||
|
||
Fetches character tower data. The `InfoType` field selects what data to return.
|
||
|
||
**Request** (`msg_mhf_get_tower_info.go`):
|
||
```
|
||
AckHandle uint32
|
||
InfoType uint32 — 1=TR/TRP, 2=TSP+skills, 3=level(pre-G7), 4=history, 5=level(G7+)
|
||
Unk0 uint32 — unknown; never used by handler
|
||
Unk1 uint32 — unknown; never used by handler
|
||
```
|
||
|
||
**Response**: variable-length array of frames via `doAckEarthSucceed`.
|
||
|
||
| InfoType | Response per frame |
|
||
|----------|-------------------|
|
||
| 1 | `TR int32, TRP int32` |
|
||
| 2 | `TSP int32, Skills [64]int16` |
|
||
| 3, 5 | `Floors int32, Unk1 int32, Unk2 int32, Unk3 int32` — one frame per era (1 for G7, 2 for G8+) |
|
||
| 4 | `[5]int16 (history group 0), [5]int16 (history group 1)` |
|
||
|
||
**InfoTypes 3 and 5** use the same code path and both return `TowerInfoLevel` entries.
|
||
The distinction between them is not understood. The `wip/tower` branch treats them
|
||
identically.
|
||
|
||
**TowerInfoLevel Unk1/Unk2/Unk3**: three of the four level-entry fields are unknown.
|
||
They are hardcoded to `5` in `wip/tower` and `0` on `develop`. Whether they carry
|
||
max floor, session count, or display state is not known.
|
||
|
||
**InfoType 4 (history)**: returns two groups of 5 × int16. The `wip/tower` branch
|
||
hardcodes them as `{1, 2, 3, 4, 5}` / `{1, 2, 3, 4, 5}`. Their meaning (e.g. recent
|
||
clear times, floor high scores) is not reverse-engineered. On `develop` they return zeros.
|
||
|
||
**Current state on develop**: Implemented. Reads from `repo_tower.GetTowerData()`.
|
||
History data is zero-filled (semantics unknown). Level Unk1–Unk3 are zero-filled.
|
||
|
||
---
|
||
|
||
### `MsgMhfPostTowerInfo` — Client → Server → Client
|
||
|
||
Submits updated tower progress after a quest.
|
||
|
||
**Request** (`msg_mhf_post_tower_info.go`):
|
||
```
|
||
AckHandle uint32
|
||
InfoType uint32 — 1 or 7 = progress update, 2 = skill purchase
|
||
Unk1 uint32 — unknown; logged in debug mode
|
||
Skill int32 — skill index to level up (InfoType=2 only)
|
||
TR int32 — new Tower Rank to set
|
||
TRP int32 — TRP earned (added to existing)
|
||
Cost int32 — TSP cost (InfoType=2) or TSP earned (InfoType=1,7)
|
||
Unk6 int32 — unknown; logged in debug mode
|
||
Unk7 int32 — unknown; logged in debug mode
|
||
Block1 int32 — floor count increment (InfoType=1,7)
|
||
Unk9 int64 — develop: reads as int64; wip/tower: reads as TimeTaken int32 + CID int32
|
||
```
|
||
|
||
**Field disambiguation — `Unk9` vs `TimeTaken + CID`**: the `wip/tower` branch splits the
|
||
final 8 bytes into `TimeTaken int32` (quest duration in seconds) and `CID int32`
|
||
(character ID). This interpretation appears more correct than a single int64 — the character
|
||
ID would make sense as a submission attribution field. `develop` keeps it as `Unk9 int64`
|
||
until confirmed. This should be verified with a packet capture.
|
||
|
||
**InfoType 7**: handled identically to InfoType 1. The difference between them is unknown
|
||
— it may relate to whether the run included Duremudira or was a normal floor clear.
|
||
|
||
**TSP rate note**: the handler comment in both branches says "This might give too much TSP?
|
||
No idea what the rate is supposed to be." The `Cost` field is used for both TSP earned
|
||
(on progress updates) and TSP spent (on skill purchases); the actual earn rate formula is
|
||
unknown.
|
||
|
||
**`block2` not written**: `UpdateProgress` only writes `block1`. The `block2` column (G7+
|
||
floor era) is never incremented by the handler. This is likely a bug — `block2` should be
|
||
written when the client sends a G7+ floor run.
|
||
|
||
**Current state on develop**: Implemented. Calls `towerRepo.UpdateSkills` (InfoType=2) and
|
||
`towerRepo.UpdateProgress` (InfoType=1,7). `Unk9`/`Unk1`/`Unk6`/`Unk7` are logged in
|
||
debug mode but not acted on.
|
||
|
||
---
|
||
|
||
### `MsgMhfGetTenrouirai` — Client → Server → Client
|
||
|
||
Fetches Tenrouirai (guild mission) data.
|
||
|
||
**Request** (`msg_mhf_get_tenrouirai.go`):
|
||
```
|
||
AckHandle uint32
|
||
Unk0 uint8 — unknown; never used
|
||
DataType uint8 — 1=mission defs, 2=rewards, 4=guild progress, 5=char scores, 6=guild RP
|
||
GuildID uint32
|
||
MissionIndex uint8 — which mission to query scores for (DataType=5 only; 1-3)
|
||
Unk4 uint8 — unknown; never used
|
||
```
|
||
|
||
**DataType=1 response**: 33 frames, one per mission definition:
|
||
```
|
||
[uint8] Block — 1–3
|
||
[uint8] Mission — type (1–6, see table above)
|
||
[uint16] Goal — score required
|
||
[uint16] Cost — RP cost to unlock/advance
|
||
[uint8] Skill1–6 — 6 skill requirement bytes (values: 80, 40, 40, 20, 40, 50)
|
||
```
|
||
|
||
**DataType=2 response (rewards)**: `TenrouiraiReward` struct is defined but never
|
||
populated. Returns an empty array. Response format:
|
||
```
|
||
[uint8] Index
|
||
[uint16] Item[0..4] — 5 item IDs
|
||
[uint8] Quantity[0..4] — 5 quantities
|
||
```
|
||
No captures of a populated reward response are known.
|
||
|
||
**DataType=4 response**: 1 frame:
|
||
```
|
||
[uint8] Page — current mission page (1-based)
|
||
[uint16] Mission1 — aggregated guild score for slot 1 (capped to goal)
|
||
[uint16] Mission2 — aggregated guild score for slot 2 (capped to goal)
|
||
[uint16] Mission3 — aggregated guild score for slot 3 (capped to goal)
|
||
```
|
||
|
||
**DataType=5 response**: N frames, one per guild member with a non-null score:
|
||
```
|
||
[int32] Score
|
||
[14 bytes] HunterName (null-padded)
|
||
```
|
||
|
||
**DataType=6 response**: 1 frame:
|
||
```
|
||
[uint8] Unk0 — always 0
|
||
[uint32] RP — guild's accumulated tower RP
|
||
[uint32] Unk2 — unknown; always 0
|
||
```
|
||
|
||
**`TenrouiraiKeyScore`** (`Unk0 uint8, Unk1 int32`): defined and included in the
|
||
`Tenrouirai` struct but never written into or sent. Likely related to an unimplemented
|
||
DataType (possibly 3). Purpose unknown.
|
||
|
||
**Current state on develop**: DataTypes 1, 4, 5, 6 implemented. DataType 2 (rewards)
|
||
returns empty. `Unk0`, `Unk4`, and `TenrouiraiKeyScore` are unresolved.
|
||
|
||
---
|
||
|
||
### `MsgMhfPostTenrouirai` — Client → Server → Client
|
||
|
||
Submits Tenrouirai results or donates guild RP.
|
||
|
||
**Request** (`msg_mhf_post_tenrouirai.go`):
|
||
```
|
||
AckHandle uint32
|
||
Unk0 uint8
|
||
Op uint8 — 1 = submit mission results, 2 = donate RP
|
||
GuildID uint32
|
||
Unk1 uint8 — unknown
|
||
|
||
Op=1 fields:
|
||
Floors uint16 — floors climbed this run
|
||
Antiques uint16 — antiques collected
|
||
Chests uint16 — chests opened
|
||
Cats uint16 — Felynes saved
|
||
TRP uint16 — TRP obtained
|
||
Slays uint16 — monsters slain
|
||
|
||
Op=2 fields:
|
||
DonatedRP uint16 — RP to donate
|
||
PreviousRP uint16 — prior RP total (from client; used for display only?)
|
||
Unk2_0–3 uint16 — unknown; 4 reserved fields
|
||
```
|
||
|
||
**Critical gap — Op=1 does nothing**: the handler logs the fields in debug mode and
|
||
returns a success ACK, but **does not write any data to the database**. Mission scores
|
||
(`guild_characters.tower_mission_1/2/3`) are never updated from quest results. This means
|
||
Tenrouirai missions can never actually advance via normal gameplay — the `SUM` aggregation
|
||
in DataType=4 will always return zero.
|
||
|
||
To fix: the handler needs to determine which of the three active missions the current run
|
||
contributes to (based on mission type and the run's stats), then write to the appropriate
|
||
`tower_mission_N` column.
|
||
|
||
**Op=2 (RP donation)**: implemented. Deducts RP from character save data, updates
|
||
`guilds.tower_rp`, and advances the mission page when the cumulative donation threshold
|
||
is met. `Unk0`, `Unk1`, and `Unk2_0-3` are parsed but unused.
|
||
|
||
**Current state on develop**: Op=2 fully implemented. Op=1 is a no-op.
|
||
|
||
---
|
||
|
||
### `MsgMhfGetGemInfo` — Client → Server → Client
|
||
|
||
Fetches gem inventory or gem acquisition history.
|
||
|
||
**Request** (`msg_mhf_get_gem_info.go`):
|
||
```
|
||
AckHandle uint32
|
||
QueryType uint32 — 1=gem inventory, 2=gem history
|
||
Unk1 uint32 — unknown
|
||
Unk2–Unk6 int32 — unknown; 5 additional fields
|
||
```
|
||
|
||
**QueryType=1 response**: 30 frames (one per gem slot):
|
||
```
|
||
[uint16] Gem — encoded as (tier << 8) | (index_within_tier + 1)
|
||
[uint16] Quantity
|
||
```
|
||
|
||
**QueryType=2 response**: N frames (gem history):
|
||
```
|
||
[uint16] Gem
|
||
[uint16] Message — purpose unknown; likely a display string ID
|
||
[uint32] Timestamp — Unix timestamp
|
||
[14 bytes] Sender — null-padded character name
|
||
```
|
||
|
||
**Current state on develop**: QueryType=1 implemented via `towerRepo.GetGems()`.
|
||
QueryType=2 returns empty (the `GemHistory` slice is never populated). `Unk1`–`Unk6`
|
||
are parsed but unused; purpose unknown.
|
||
|
||
---
|
||
|
||
### `MsgMhfPostGemInfo` — Client → Server → Client
|
||
|
||
Adds or transfers gems.
|
||
|
||
**Request** (`msg_mhf_post_gem_info.go`):
|
||
```
|
||
AckHandle uint32
|
||
Op uint32 — 1=add gem, 2=transfer gem
|
||
Unk1 uint32 — unknown
|
||
Gem int32 — gem ID encoded as (tier << 8) | (index+1)
|
||
Quantity int32 — amount
|
||
CID int32 — target character ID (Op=2 likely uses this)
|
||
Message int32 — display message ID? purpose unknown
|
||
Unk6 int32 — unknown
|
||
```
|
||
|
||
**Op=1 (add gem)**: implemented. Decodes the gem index from the `Gem` field, increments
|
||
the quantity in the CSV, and saves. Note: the index computation `(pkt.Gem >> 8 * 5) + (pkt.Gem - pkt.Gem&0xFF00 - 1%5)` may have operator precedence issues — verify with captures.
|
||
|
||
**Op=2 (transfer gem)**: not implemented. Handler comment: *"no way im doing this for now"*.
|
||
The `CID` field likely identifies the recipient character. Format of the response (if any
|
||
acknowledgement is sent to the recipient) is unknown.
|
||
|
||
**Current state on develop**: Op=1 implemented via `towerService.AddGem()`. Op=2 stub.
|
||
`Unk1`, `Message`, `Unk6` purposes unknown.
|
||
|
||
---
|
||
|
||
### `MsgMhfPresentBox` — Client → Server → Client
|
||
|
||
Fetches or claims items from the Tower present box (a reward inbox for seibatsu/Tower
|
||
milestone awards). This packet's field names differ between `develop` and `wip/tower`:
|
||
|
||
**Request** — `wip/tower` naming (more accurate than develop's all-`Unk*` version):
|
||
```
|
||
AckHandle uint32
|
||
Unk0 uint32
|
||
Operation uint32 — 1=open list, 2=claim item, 3=close
|
||
PresentCount uint32 — number of PresentType entries that follow
|
||
Unk3 uint32 — unknown
|
||
Unk4 uint32 — unknown
|
||
Unk5 uint32 — unknown
|
||
Unk6 uint32 — unknown
|
||
PresentType []uint32 — array of present type IDs (length = PresentCount)
|
||
```
|
||
|
||
On `develop`, `Operation` is `Unk1` and `PresentCount` is `Unk2` — the field is correctly
|
||
used to drive the `for` loop but the semantic name is lost.
|
||
|
||
**Response** for Op=1 and Op=2: N frames via `doAckEarthSucceed`, each:
|
||
```
|
||
[uint32] ItemClaimIndex — unique claim ID
|
||
[int32] PresentType — echoes the request PresentType
|
||
[int32] Unk2–Unk7 — 6 unknown fields (always 0 in captures)
|
||
[int32] DistributionType — 7201=item, 7202=N-Points, 7203=guild contribution
|
||
[int32] ItemID
|
||
[int32] Amount
|
||
```
|
||
|
||
**Critical gap — no claim tracking**: `ItemClaimIndex` is a sequential ID that the client
|
||
uses for "claimed" state, but the server has no DB table or flag for it. Every call
|
||
returns the same hardcoded items, so a player can claim the same rewards repeatedly.
|
||
|
||
**Op=3**: returns an empty buffer (close/dismiss).
|
||
|
||
**Current state on develop**: handler returns an empty item list (`data` slice is nil).
|
||
The `wip/tower` branch has a working hardcoded handler (7 dummy items per `PresentType`)
|
||
with the correct response structure. `Unk0`, `Unk3`–`Unk6` purposes unknown.
|
||
|
||
**Note**: `wip/tower`'s `MsgMhfPresentBox.Parse()` still contains `fmt.Printf` debug
|
||
print statements that must be removed before any merge.
|
||
|
||
---
|
||
|
||
### `MsgMhfGetNotice` — Client → Server → Client
|
||
|
||
Purpose unknown. Likely fetches in-lobby Tower notices or announcements.
|
||
|
||
**Request** (`msg_mhf_get_notice.go`):
|
||
```
|
||
AckHandle uint32
|
||
Unk0 uint32
|
||
Unk1 uint32
|
||
Unk2 int32
|
||
```
|
||
|
||
**Current state**: Stub on `develop` — returns `{0, 0, 0, 0}`. Response format unknown.
|
||
|
||
---
|
||
|
||
### `MsgMhfPostNotice` — Client → Server → Client
|
||
|
||
Purpose unknown. Likely submits a read-receipt or acknowledgement for a Tower notice.
|
||
|
||
**Request** (`msg_mhf_post_notice.go`):
|
||
```
|
||
AckHandle uint32
|
||
Unk0 uint32
|
||
Unk1 uint32
|
||
Unk2 int32
|
||
Unk3 int32
|
||
```
|
||
|
||
**Current state**: Stub on `develop` — returns `{0, 0, 0, 0}`. Response format unknown.
|
||
|
||
---
|
||
|
||
## What Is Already Working on Develop
|
||
|
||
- Character tower data (TR, TRP, TSP, skills, floor counts) is read and written via the
|
||
full repository pattern.
|
||
- Skill levelling (InfoType=2) deducts TSP and increments the correct CSV index.
|
||
- Floor progress (InfoType=1,7) updates TR, TRP, TSP, and block1.
|
||
- All 33 Tenrouirai mission definitions are hardcoded and served correctly.
|
||
- Guild Tenrouirai progress (page, aggregated mission scores) is read and score-capped.
|
||
- Per-character Tenrouirai leaderboard (DataType=5) is read from DB.
|
||
- Guild tower RP donation (Op=2) deducts player RP, accumulates guild RP, and advances
|
||
the mission page when the threshold is met.
|
||
- Gem inventory (QueryType=1) is read and returned correctly.
|
||
- Gem add (Op=1) updates the CSV at the correct index.
|
||
|
||
---
|
||
|
||
## What Needs RE or Implementation
|
||
|
||
### Functional bugs (affect gameplay today)
|
||
|
||
| Issue | Location | Notes |
|
||
|-------|----------|-------|
|
||
| `PostTenrouirai` Op=1 is a no-op | `handlers_tower.go` | Mission scores are never written; Tenrouirai cannot advance via normal play |
|
||
| `block2` never written | `repo_tower.go → UpdateProgress` | G7+ floor count not persisted; requires captures to confirm which InfoType sends it |
|
||
| `PresentBox` returns empty list | `handlers_tower.go` | No items are ever shown; `wip/tower` handler logic can be adapted |
|
||
| Present claim tracking absent | DB schema | No "claimed" flag; players can re-claim indefinitely once handler is populated |
|
||
|
||
### Unknown packet fields (need captures to resolve)
|
||
|
||
| Field | Packet | Notes |
|
||
|-------|--------|-------|
|
||
| `Unk9 int64` vs `TimeTaken int32 + CID int32` | `MsgMhfPostTowerInfo` | `wip/tower` splits this into two int32; likely correct — needs capture confirmation |
|
||
| `Unk1`, `Unk6`, `Unk7` | `MsgMhfPostTowerInfo` | Logged in debug but unused; may encode run metadata |
|
||
| `Unk0`, `Unk1` | `MsgMhfGetTowerInfo` | Never used in handler |
|
||
| `TowerInfoLevel.Unk1–Unk3` | `GetTowerInfo` InfoType=3,5 | 3 of 4 level-entry fields zero-filled; may be max floor, session count, display state |
|
||
| `TowerInfoHistory` 10 × int16 | `GetTowerInfo` InfoType=4 | Two groups of 5; semantics unknown (recent clear times? floor high scores?) |
|
||
| InfoType 3 vs 5 distinction | `MsgMhfGetTowerInfo` | Same code path; difference not understood |
|
||
| InfoType 7 vs 1 distinction | `MsgMhfPostTowerInfo` | Both update progress; difference not understood |
|
||
| `Unk0`, `Unk4` | `MsgMhfGetTenrouirai` | Always 0 in known captures |
|
||
| `TenrouiraiKeyScore` | `GetTenrouirai` | Struct defined, never sent; likely an unimplemented DataType |
|
||
| `Unk0`, `Unk1`, `Unk2_0–3` | `MsgMhfPostTenrouirai` | 6 parsed fields that are never used |
|
||
| `Unk1–Unk6` | `MsgMhfGetGemInfo` | 6 extra fields; may filter by tier, season, or character |
|
||
| `Message`, `Unk1`, `Unk6` | `MsgMhfPostGemInfo` | `Message` likely a display string ID for gem transfer notices |
|
||
| `Unk0`, `Unk3–Unk6` | `MsgMhfPresentBox` | 5 unknown request fields |
|
||
| All fields | `MsgMhfGetNotice`, `MsgMhfPostNotice` | Both packets entirely uncharacterized |
|
||
|
||
### Missing features (require further RE + design)
|
||
|
||
| Feature | Notes |
|
||
|---------|-------|
|
||
| Tenrouirai mission score submission | `PostTenrouirai` Op=1 needs to map run stats to the correct mission type and write `tower_mission_N` |
|
||
| Tenrouirai rewards (DataType=2) | `TenrouiraiReward` response format is known; item IDs and quantities are not |
|
||
| Gem transfer (PostGemInfo Op=2) | Recipient lookup via `CID`; likely requires a notification to the target session |
|
||
| Gem history (GetGemInfo QueryType=2) | Response structure is known; DB storage is not — would require a `gem_history` table |
|
||
| PresentBox claim tracking | Needs a `present_claims` table or a bitfield on the character |
|
||
| Notice system | Both Get/Post are stubs; may be Tower bulletin board or reward notifications |
|
||
|
||
---
|
||
|
||
## Relation to the Conquest War Doc
|
||
|
||
`docs/conquest-war.md` covers the `GetWeeklySeibatuRankingReward` handler which already
|
||
implements the Tower dure kill reward table (Op=5, ID 260001) and the Tower floor reward
|
||
table (Op=5, ID 260003). Those are not missing here — they live in `handlers_seibattle.go`
|
||
on the `feature/conquest` branch. When that branch is eventually integrated, ensure the
|
||
Tower floor reward data is preserved.
|