AquaDX/docs/dev/sinmai_dev_notes.md
2026-05-12 03:59:09 +00:00

168 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# maimai2 dev notes
## Item types
| ItemKind | Name |
|----------|-------------------------|
| 1 | Nameplate |
| 2 | Title |
| 3 | Icon |
| 5 | Music Unlock |
| 6 | Music Master Unlock |
| 7 | Music Re:Master Unlock |
| 8 | Music Strong Unlock (?) |
| 9 | Character |
| 10 | Partner |
| 11 | Frame |
| 12 | Tickets |
| 13 | Mile |
| 14 | Intimate Item |
| 15 | Kaleidx Scope Key |
## Kaleidx Scope gates
Kaleidx Scope gate visibility is controlled by three server responses:
* `GetGameEventApi` must return open event rows for the gate/course event IDs used by the game's static data.
* `GetGameKaleidxScopeApi` must return a `gameKaleidxScopeList` row for each gate the user can see. Current known gate IDs are `1..6` for the original gates, `7` for Prism/Center Tower, `8` for the post-Prism boss gate, `9` for Hope Gate, and `10` for the final challenge.
* `GetUserKaleidxScopeApi` must return a `userKaleidxScopeList` row for the same `gateId`.
The client hides a gate when the user row is missing, the event is closed, or the row has `isGateFound = false`. A visible selectable gate needs at least:
```json
{
"gateId": 8,
"isGateFound": true,
"isKeyFound": true,
"isClear": false
}
```
The unlock announcement can still appear when `isGateFound = true`, `isKeyFound = true`, and `isInfoWatched = false`; that announcement alone does not prove that the gate will be listed in the menu on the next download.
For progression past Prism Tower, `UpsertUserAllApi` must persist `upsertUserAll.userKaleidxScopeList` by `(userId, gateId)`. AquaDX unlocks Prism/Center Tower gate `7` after gate `6` is clear. After gate `7` is clear, the next `GetUserKaleidxScopeApi` response must include gate `7` as clear and a gate `8` row with both found/key true. The same pattern applies for gates `9` and `10` after their preceding gates are clear.
Fixed AquaDX.v1 issue: older code advertised gates `1..10` from `GetGameKaleidxScopeApi` and saved uploaded Kaleidx rows in `UpsertUserAllApi`, but `GetUserKaleidxScopeApi` only backfilled missing rows for gates `1..6`. It also forced every persisted row to `isKeyFound = true` before returning it. Existing users without usable rows for gate `7` or later could therefore receive an unlock-style message but still have no visible Prism Tower or post-Prism gate in the Kaleidx Scope menu.
To confirm the failure for a user, inspect their rows:
```sql
SELECT gate_id, is_gate_found, is_key_found, is_clear, is_info_watched
FROM maimai2_user_kaleidx
WHERE user_id = <internal_user_id>
ORDER BY gate_id;
```
If gate `7` is clear and gate `8` is missing, or if any target gate row has `is_gate_found = 0`, the menu will not show the next gate.
## Multiplayer
### Party Host/Client/Member
Manager.Party.Party/**Host.cs** : Host :
* TCP **Listen** 50100 (Accept into Member)
* UDP Broadcast 50100
* Send: StartRecruit, FinishRecruit
PartyLink/**Party.cs** : Party.Host : Exact same as Host.cs
Manager.Party.Party/**Member.cs** : Member :
* TCP Connect 50100
* Send: JoinResult, Kick, StartPlay, StartClientState, PartyMember{Info/State}, PartyPlayInfo, RequestMeasure
* Recv: RequestJoin, ClientState, ClientPlayInfo, UpdateMechaInfo, ResponseMeasure, FinishNews
PartyLink/**Party.cs** : Party.Member : Exact same as Member.cs
Manager.Party.Party/**Client.cs** : Client :
* UDP **Listen** 50100
* Recv: StartRecruit, FinishRecruit
* TCP Connect 50100
* Recv: JoinResult, Kick, StartPlay, StartClientStatePartyMember{Info/State}, PartyPlayInfo, RequestMeasure
* Send: RequestJoin, ClientState, ClientPlayInfo, UpdateMechaInfo, ResponseMeasure, FinishNews
PartyLink/**Party.cs** : Party.Client : Exact same as Client.cs
**Enums**
* **ClientStateID**: {Setup, Wait, Connect, Request, Joined, FinishSetting, ToReady, BeginPlay, AllBeginPlay, Ready, Sync, Play, FinishPlay, News, NewsEnd, Result, Disconnected, Finish, Error}
* **JoinResult**: {Success, Full, NoRecruit, Disconnect, AlreadyJoined, DifferentGroup, DifferentMusic, DifferentEventMode}
**Models**
* **MechaInfo**: IsJoin (bool), IP Address, MusicID, Entries[2], UserIDs[2], Rating[2], ...
* **RecruitInfo**: MechaInfo, MusicID, GroupID, EventModeID, JoinNumber, PartyStance, Start time, Recv time
* **MemberPlayInfo**: IP Address, Rankings[2], Achieves[2], Combos[2], Miss[2], ...
* **ChainHistory**: PacketNo (int), Chain (int)
**Commands**
* **StartRecruit/FinishRecruit**: RecruitInfo
* **JoinResult**: JoinResult (enum)
* **RequestJoin**: MechaInfo, GroupID, EventModeID
* **UpdateMechaInfo**: MechaInfo
* **Kick**: RecruitInfo, KickBy {Cancel, Start, Disconnect}
* **RequestMeasure/ResponseMeasure**: {} - Sync delay
* **StartPlay**: MaxMeasure (long), MyMeasure (long) - Sync delay
* **StartClientState**: ClientStateID (enum)
* **ClientState**: ClientStateID (enum)
* **PartyMemberInfo**: MechaInfo[2]
* **PartyMemberState**: ClientStateID[2]
* **PartyPlayInfo**: MemberPlayInfo[2], ChainHistory[10], Chain (int), ChainMiss (int), MaxChain (int), IsFullChain (bool), CalcStatus (int)
* **ClientPlayInfo**: IP Address, Count, IsValids[2], Achieves[2], Combos[2], Miss[2], ...
* **FinishNews**: IP Address, IsValids[2], GaugeClears[2], GaugeStockNums[2]
### Setting Host/Client/Member
> This might be for synchronizing event settings across different cabs,
> I'm not sure if this is relevant for multiplayer.
PartyLink/**Setting.cs** : Setting.**Host** :
* TCP **Listen** 50101 (Accept into Setting.Member)
* UDP Broadcast 50101
* Send: SettingHostAddress
* UDP **Listen** 50101
* Recv: SettingHostAddress (Check duplicate host)
PartyLink/**Setting.cs** : Setting.**Client** :
* TCP Connect 50101
* Send: SettingRequest
* Recv: SettingResponse, HeartBeat{}
* UDP **Listen** 50101
* Recv: SettingHostAddress
PartyLink/**Setting.cs** : Setting.**Member** :
* TCP Connect 50101
* Recv: SettingRequest, HeartBeat{}
* Send: SettingResponse, HeartBeat{}
**Models**
* **SettingHostAddress**: IP Address (u32), Group (int)
* **SettingRequest**: Group (int)
* **SettingResponse**: Group (int), Data (isEventMode, eventModeMusicCount, memberNumber)
### Advertise
> For finding IP addresses of other cabs and checking their latency.
PartyLink/**Advertise.cs** : Advertise.Manager :
* UDP **Listen** 50102
* Recv: AdvertiseRequest, AdvertiseResponse, AdvertiseGo
* UDP Broadcast 50102
* Send: AdvertiseRequest, AdvertiseResponse, AdvertiseGo
**Models**
* **AdvertiseRequest**: IP Address (u32), Group (int), Kind (int)
* **AdvertiseResponse**: IP Address (u32), Group (int), Kind (int)
* **AdvertiseGo**: IP Address (u32), Group (int), Kind (int), MaxUsec (long), MyUsec (long)
!! sendTo is not necessarily broadcast !!
### DeliveryChecker
PartyLink/**DeliveryChecker** : DeliveryChecker.Manager :
* UDP **Listen** 50103
* Recv: AdvocateDelivery
* UDP Broadcast 50103
* Send: AdvocateDelivery
**Models**
* **AdvocateDelivery**: IP Address (u32)