Add fort-attack-event.md capturing everything known about the fort attack event system (packet wire formats, register plumbing, DB schema gap, quest IDs) and what needs reverse-engineering before implementation is possible. The feature/enum-event branch covered only scheduling and is not mergeable; its findings are preserved here for future reference. Update unimplemented.md to point to the new doc and correctly describe the branch scope.
8.4 KiB
Fort Attack Event (迎撃拠点 / Interceptor's Base)
Tracks what is known about the Interceptor's Base fort attack event system and what remains to be reverse-engineered before it can be implemented in Erupe.
The feature/enum-event branch (origin) attempted a partial implementation but is not mergeable in
its current state. Its useful findings are incorporated below.
Game Context
The Interceptor's Base (迎撃拠点) is a persistent field introduced in Forward.1 (April 2011). Guilds defend a fortress adjacent to Mezeporta against invading Elder Dragons. The fort has a durability meter — if monster attacks reduce it to 0% the quest fails regardless of time or lives remaining. Monsters known to attack include Rukodiora, Rebidiora, Teoleskatle, Yamatukami, Shengaroren, Harudomerugu, Rusted Kushala Daora, Belkyruros, Abiologu, and Keoaruboru.
Keoaruboru (古龍の侵攻 culmination, added MHF-Z Z1.1) is the hardest variant. Its limbs accumulate heat as the fight progresses; if any limb reaches maximum heat it fires a beam at the fort dealing 20% durability damage and resetting all heat. The fort starts at 80% integrity, meaning four unchecked beams cause quest failure. Managing heat across limbs is the central mechanic.
The event was scheduled by Capcom's live servers on a cycle. The exact trigger frequency is not publicly documented in either English or Japanese sources.
Packet Overview
Five packets are involved. All live in network/mhfpacket/.
MsgMhfEnumerateEvent (0x72) — Client → Server → Client
The client polls this on login to learn what fort attack events are currently scheduled.
Request (msg_mhf_enumerate_event.go): AckHandle uint32 + two zeroed uint16.
Response built in handleMsgMhfEnumerateEvent (handlers_event.go):
[uint8] event count
for each event:
[uint16] EventType — 0 = nothing; 1 or 2 = "Ancient Dragon has attacked the fort"
[uint16] Unk1 — unknown; always 0 in known captures
[uint16] Unk2 — unknown; always 0
[uint16] Unk3 — unknown; always 0
[uint16] Unk4 — unknown; always 0
[uint32] StartTime — Unix timestamp (seconds) when event begins
[uint32] EndTime — Unix timestamp when event ends
if EventType == 2:
[uint8] quest file count
[uint16] quest file ID × N
What EventType == 1 means vs EventType == 2 is not known. The quest file ID list only appears
when EventType == 2. The semantics of Unk1–Unk4 are entirely unknown.
Current state: Handler returns an empty event list (0 events). The feature/enum-event branch
adds DB-backed scheduling with a configurable Duration / RestartAfter cycle and a hardcoded
list of 19 quest IDs, but has a logic bug (inverted rows.Next() check) and uses raw DB calls
instead of the repo pattern.
MsgMhfRegisterEvent — Client → Server → Client
Sent when a player attempts to join a fort attack session.
Request (msg_mhf_register_event.go):
AckHandle uint32
Unk0 uint16 — unknown
WorldID uint16
LandID uint16
CheckOnly bool — if true, only check whether an event is active (don't join)
[uint8 zeroed padding]
Response (4 bytes): WorldID uint8 | LandID uint8 | RaviID uint16
Current state: Implemented in handlers_register.go. On CheckOnly=true with no active
Raviente semaphore it returns a zeroed 4-byte success. Otherwise it echoes back the world/land IDs
and s.server.raviente.id. This is the Raviente siege plumbing reused — whether it is correct for
fort attack (as opposed to the Raviente siege proper) is unknown.
MsgMhfReleaseEvent — Client → Server
Sent when a player leaves a fort attack session. Carries RaviID uint32 (the session ID returned
by RegisterEvent) plus a zeroed uint32.
Current state: Always returns _ACK_EFAILED (0x41). The correct success response format is
unknown — packet Build() is also unimplemented.
MsgMhfGetRestrictionEvent — Client → Server → Client
Purpose unknown. Likely fetches per-player or per-world restrictions for event participation (e.g. quest rank gate, prior completion check).
Current state: Packet Parse() and Build() both return NOT IMPLEMENTED. Handler is an
empty no-op (handleMsgMhfGetRestrictionEvent). No captures of this packet are known.
MsgMhfSetRestrictionEvent — Client → Server → Client
Purpose unknown. Likely sets restriction state after an event completes or a player qualifies.
Request (msg_mhf_set_restriction_event.go):
AckHandle uint32
Unk0 uint32 — unknown
Unk1 uint32 — unknown
Unk2 uint32 — unknown
Unk3 uint8 — unknown
Current state: Handler returns a zeroed 4-byte success. Build() is unimplemented. Packet
semantics are entirely unknown.
Shared State (Registers)
The Raviente siege uses three named register banks (raviRegisterState, raviRegisterSupport,
raviRegisterGeneral) served via MsgSysLoadRegister and mutated via MsgSysOperateRegister.
The fort attack event likely uses the same register mechanism for shared state (fort durability,
Keoaruboru heat accumulation, etc.), but which register IDs and slot indices map to which fort
variables has not been reverse-engineered.
handleMsgSysNotifyRegister is a stub (// stub: unimplemented) — this handler broadcasts
register updates to other players in the session. It must be implemented for multi-player fort
state synchronisation to work.
Database
The events table (server/migrations/sql/0001_init.sql) already supports timestamped events:
CREATE TABLE public.events (
id SERIAL PRIMARY KEY,
event_type event_type NOT NULL,
start_time TIMESTAMP WITH TIME ZONE NOT NULL
);
The event_type enum currently contains festa, diva, vs, mezfes. Adding ancientdragon
requires a migration:
ALTER TYPE event_type ADD VALUE 'ancientdragon';
The feature/enum-event branch placed this in schemas/patch-schema/event-ancientdragon.sql,
which is outside the numbered migration sequence and will not be auto-applied. It needs to be
added as server/migrations/sql/0002_ancientdragon_event_type.sql (or folded into the next
migration).
What Needs RE Before Implementation
| Unknown | Where to look | Priority |
|---|---|---|
Semantics of EventType values (1 vs 2, others?) |
Packet captures during event window | High |
| Meaning of Unk1–Unk4 in the EnumerateEvent response | Packet captures + client disassembly | Medium |
Correct MsgMhfReleaseEvent success response format |
Packet captures | High |
MsgMhfGetRestrictionEvent full structure (parse + response) |
Packet captures | High |
MsgMhfSetRestrictionEvent field semantics (Unk0–Unk3) |
Packet captures | Medium |
| Which register IDs / slots carry fort durability | Packet captures during fort quest | High |
| Keoaruboru heat accumulation register mapping | Packet captures during Keoaruboru quest | High |
Whether MsgMhfRegisterEvent reuses Raviente state correctly for fort |
Packet captures + comparison with Raviente behaviour | Medium |
| Original event scheduling cadence (cycle length, trigger time) | Live server logs / JP wiki sources | Low |
What Is Already Understood
MsgMhfEnumerateEventresponse wire format (field order, types, conditional quest ID list)StartTime/EndTimeare Unix timestamps (confirmed by the feature branch)MsgMhfRegisterEventrequest structure and plausible response format (echoes world/land + ravi ID)MsgMhfReleaseEventrequest structure (carries the ravi session ID)MsgMhfSetRestrictionEventrequest structure (5 fields, semantics unknown)- The fort event cycles via the
eventstable and can share the existing Raviente semaphore infrastructure - Quest file IDs for fort quests:
20001, 20004–20006, 20011–20013, 20018–20029(from feature branch config; unvalidated against captures)
Relation to Raviente
The Raviente siege (sys_channel_server.go, handlers_register.go) is the closest implemented
analogue. It uses the same MsgMhfRegisterEvent / MsgSysOperateRegister / MsgSysLoadRegister
pipeline. Fort attack implementation can likely reuse or extend this infrastructure rather than
building a separate system. The key difference is that Raviente is always available (with its own
scheduling), while fort attacks are event-gated via MsgMhfEnumerateEvent.