The previous article roughly covered maimai’s boot initialisation flow; this one digs into the cabinet networking part that has troubled quite a few people
1. Overview
1.1 Where the self-check sits in the boot chain
In the previous initialisation article we covered that a maimai cabinet runs two parallel systems: amdaemon (the native background service, responsible for all communication with the operations server) and the Unity game program (responsible for the game)
Boot proceeds in 6 steps, and the network self-check is step 3 (PowerOnProcess.WaitNetWork). The boot initialisation screen shows the status of each check line by line: Check 初始化中 / Good 好 / NA 跳过 / Bad 坏
The goal of the network self-check is to confirm whether the cabinet can communicate with the server normally; the first 5 checks cover the cabinet itself and the LAN, and the last 4 cover connectivity to and liveness of the server
1.2 Chain overview
[Boot] amdaemon starts └─ ALPB boot self-check driver └─ am::network::Tester runs the 9 network self-checks in sequence (am::networktest) 0 IpAddressChecker ← validity of the local IP 1 GatewayChecker ← gateway connectivity 2 DnsLanChecker ← validity of the DNS configuration 3 HopsChecker ← domain resolution + ICMP route hop count 4 LineTypeChecker ← line type (LAN/WAN) 5 AllnetAuthChecker ← ALL.Net auth + download instruction ready (120s) 6 AimeChecker ← CHIME WeChat AIME service liveness probe 7 EmoneyChecker ← e-money (dead item on the mainland server, NA) 8 AimePayChecker ← AimePay (dead item on the mainland server, NA) │ each result reported via callback ▼ amdaemon app-side NetworkEventListener → availability flags (IP / Hops / AllnetAuth) ▼ IPC (NamedPipe)[Unity] AMDaemon.Network.TestInfo / PowerOnTestInfo ▼ polled every frame PowerOnProcess.WaitNetWork (item0 timeout 15s / item5 Net auth 120s)1.3 The 9 checks at a glance
| item | checker class name | what it checks | main dependency |
|---|---|---|---|
| 0 | IpAddressChecker | Whether the local IP is valid (not APIPA) | Local / DHCP |
| 1 | GatewayChecker | Whether the gateway is pingable | Local LAN |
| 2 | DnsLanChecker | Whether the count of system DNS configs is valid | Local LAN |
| 3 | HopsChecker | Whether the target domain resolves + ICMP hop-by-hop count | DNS + target domain |
| 4 | LineTypeChecker | Whether the line type is LAN | Local network interface |
| 5 | AllnetAuthChecker | ALL.Net auth + download instruction ready | ALL.Net server (120s) |
| 6 | AimeChecker | Member server liveness | CHIME server |
| 7 | EmoneyChecker | Emoney server liveness (dead item on the mainland server) | None |
| 8 | AimePayChecker | AimePay server liveness (dead item on the mainland server) | None |
2. Mechanism basics
2.1 Three-layer structure
| Layer | Role | Description |
|---|---|---|
| Native engine layer (am::network) | Actually performs network operations | Embeds the various engine checkers (IpAddr / Gateway / DnsLan / LocationRouter etc.) |
| Checker layer (am::networktest) | Wrapping and judging | The master Tester strings together the 9 item checkers, each holding a pointer to its corresponding engine |
| Game program layer (Unity C#) | Reading and displaying | Polls the status word every frame, translating it into Good / NA / Bad / Check |
The actual network operations are performed in the am::network::* engine classes; am::networktest::* is the wrapper layer, responsible for driving the engines and reporting the results to the game program
2.2 Execution state machine
Each engine checker maintains an internal asynchronous stepper (Waiter), whose lifecycle is:
clear → enqueue (attach the check target) → start→ step each round → judge whether complete → take the OK/NG flag → write out the resultResult codes: 4 = OK (passed), 5 = NG (failed), 1 = waiting / skipped
2.3 Core fields of an item checker
Each item checker object contains the following key fields:
- item number (0–8)
- state slot: 2 = OK, 3 = running
- engine result snapshot: copied in once the engine finishes, for the judgement logic to read
- display status word: the game program reads this byte every frame to decide what is shown on screen
2.4 Drive loop
The master Tester walks through the 9 items in order each round:
- Read the current item’s state slot; skip if already OK
- First round: copy in the engine result snapshot, write the state as “running”, trigger the callback
- Afterwards: keep stepping the engine until the result is ready
- Once all nine are complete, the Tester’s overall state is set to complete
2.5 shouldRun predicate
Each item has a shouldRun function that decides whether the item needs to run. If it returns false (e.g. there’s no corresponding configured target), the item is skipped outright and the game side shows NA. On the mainland server, item7 (Emoney) and item8 (AimePay) both return false for this reason, and the check item simply isn’t displayed at all
3. The 9 checks one by one
3.0 IP address (IpAddressChecker)
Engine class: am::network::IpAddressChecker
Judgement logic:
Read the current IP address of the local network card and judge by address range:
169.254.x.x(APIPA) → NG: this is the temporary address the operating system assigns automatically when DHCP fails or the network cable is disconnected, meaning the cabinet hasn’t obtained a valid IP- The specified private address ranges → OK
- Network card unconfigured (
ignore_unconnectedswitch triggered) → NA, skipped
Timeout: the game program side sets a 15-second timeout for this item; on timeout it’s judged Bad outright
3.1 Gateway (GatewayChecker)
Engine class: am::network::GatewayChecker (inherits from GatewayPreChecker)
Judgement logic:
Send an ICMP ping to the gateway address in the configuration and wait for a response:
- ping succeeds → OK
- ping fails → back off and retry, with the interval starting at 60 seconds and doubling each time, capped at about 900 seconds (about 4 retries); after all fail it’s judged NG
The underlying layer uses ICMP (altr_icmp). In other words, if the gateway router is pingable it passes; a wrong gateway IP, a broken switch or a physically disconnected cable ends in Bad
3.2 Local DNS (DnsLanChecker)
Engine class: am::network::DnsLanChecker
Judgement logic:
Read the number of DNS servers in the configuration and compare it with the number of DNS servers actually discoverable on the system:
- The actual number meets the configuration requirement → OK
- It doesn’t (e.g. DNS wasn’t delivered or the config is wrong) → waiting / NG
The underlying layer calls the system DNS resolution interface (amDnsInit / amDnsResolveNameA/W). However many DNS servers are written in the config, that many have to be discoverable on the system; if DNS isn’t configured or DHCP didn’t deliver it, it fails
3.3 Hops (HopsChecker)
Engine class: am::network::HopsCounter (the actual resolution is driven by NameResolver)
Judgement logic:
- DNS-resolve the target hostname
- After a successful resolution, count hops via ICMP
- On the C# side, item3 displays the actual hop count
- DNS resolution fails → NG
- Resolution succeeds → hop count displayed and OK
It depends on item2 (DnsLan) being normal: if the domain resolves it passes, and the route hop count is displayed at the same time; if it can’t resolve, it’s Bad.
3.4 Line type (LineTypeChecker)
Engine class: am::network::LocationRouterChecker
Judgement logic:
Detect the current line type:
- LAN → OK
- WAN → NG, error message
Network type error(WAN)
Internally the engine judges the line’s attribution by parsing the network address and subnet mask, and the underlying layer depends on NameResolver to resolve the target address
3.5 Net auth (AllnetAuthChecker)
Engine class: alw_auth (the ALL.Net authentication library)
Judgement logic (two conditions, both must be satisfied):
- Auth passed: complete the authentication handshake with the ALL.Net server
- Download instruction ready: obtain the netdeliver download instruction URI from the server
If either condition isn’t met → it stays in the Check state and the engine keeps stepping; the game side sets a hard 120-second timeout, and on timeout it’s judged Bad outright
Auth chain stages: DNS resolution → auth handshake → download instruction retrieval. If any link has a communication problem with the server, it hangs in Check until it times out
An item5 Bad blocks the subsequent title server config download flow and the cabinet can’t reach the game screen, so the entire ALL.Net auth flow must complete for it to pass
3.6 CHIME (AimeChecker)
Engine: Aime network client (AimeNetworkClient)
Judgement logic:
- Check whether it’s in progress (the engine state meets the running condition)
- Read the first character of the CHIME client’s liveness result area:
- equal to 0 → OK
- non-zero (error code) → running / NG
On the game side this corresponds to Chime.Instance.IsDBAlive, i.e. the database liveness probe for the WeChat AIME service
The later WaitAime stage (Chime.StartDBAliveCheck) probes once more; the two share the same liveness interface
3.7 Emoney (EmoneyChecker) — dead item on the mainland server
Engine: EmoneyNetworkClient
Judgement logic: first character of the result area equal to 0 → OK, non-zero → NG (same structure as AimeChecker).
Mainland server: no configured target, shouldRun returns false, permanently NA, skipped. There’s no corresponding display slot in the game UI (that position is reused for the TitleServer liveness probe, using OperationManager.IsAliveServer)
3.8 AimePay (AimePayChecker) — dead item on the mainland server
Engine: AimePayCheckInSequencer
Judgement logic: first character of the result area equal to 0 → OK, non-zero → NG
Mainland server: same as Emoney — no configured target, permanently NA, skipped, no display slot in the game screen
4. Result reporting and on-screen display
4.1 Transmission chain
Engine result (OK / NG) → item result area snapshot (copied in once the engine finishes) → item display status word (read by the game program every frame) → network availability flags (IP / Hops / AllnetAuth reported separately) → IPC (NamedPipe) → C# WaitNetWork translates each item into a screen statusThe three availability flags (IP, Hops, AllnetAuth) are maintained separately on the amdaemon app side, for later flows to judge the network’s readiness state
4.2 Game translation rules
| Condition | Display |
|---|---|
| Not busy and not complete | NA |
| Busy and the native side has already judged NG | Bad 坏 |
| Busy and item0 (IP) exceeds 15 seconds | Bad 坏 |
| Busy and item5 (Net auth) exceeds 120 seconds | Bad 坏 |
| Busy and anything else | Check 初始化中 (blinking) |
| Complete and passed | Good 好 (item3 additionally shows the hop count) |
| Complete and skipped | NA |
| Complete and failed | Bad 坏 |
The timeout fallback is in the game layer (Stopwatch); the error semantics are in the native layer
4.3 Notes on the game display side
TestModePageNetwork.ItemEnum has 8 positions in total, with the following mapping:
- item0–5: read
Network.TestInfodirectly, corresponding to IpAddress / Gateway / LocalDns / HopNum / LineType / AllnetAuth - item6 (Aime): read
Chime.Instance.IsDBAlive, i.e. the CHIME WeChat AIME liveness result - item7 (TitleServer): read
OperationManager.IsAliveServer, unrelated to native item7 Emoney (the mainland server reuses that slot)
There is no corresponding position in EnumEnum for Emoney / AimePay
4.4 Dependencies of later flows
WaitNetWork any Bad or complete → NetWorkReady → WaitAime (Chime.StartDBAliveCheck probes once more) → AimeReady → OperationManager.StartTest() (download config) → WaitTitleServer (download succeeded and title server alive) → Released: enter normal game schedulingAn item5 (Net auth) Bad blocks the download in the WaitTitleServer stage; a Bad on the other items doesn’t directly block the flow but affects later functionality
4.5 Title server (TitleServer) check notes
title is not, strictly speaking, a native network self-check. All of the previous 9 items (item0-8) are executed by amdaemon’s native am::network / am::networktest system and belong to WaitNetWork; the native side has no title check item: the title server isn’t in amdaemon’s boot network self-check enumeration, and is unrelated to item7 Emoney either
But the boot flow does check it. The title check happens after the network self-check and before entering normal game scheduling, driven by the Unity game side’s OperationManager:
WaitNetWork complete → NetWorkReady → WaitAime (CHIME probes once more) → AimeReady → OperationManager.StartTest() (download config from the title server) → WaitTitleServer (download succeeded once + title server alive) → Released- Judgement condition:
WasDownloadSuccessOnce && IsAliveServer— the title config has been downloaded successfully at least once, and the title server is currently alive - Who checks it: the Unity game side (
OperationManager), not the native self-check framework - Stuck symptom: all 9 network self-checks are Good but it still sits on the title screen → most likely stuck at
WaitTitleServer - Test menu: the 8th position of
TestModePageNetworkshows “TitleServer”, readingOperationManager.IsAliveServer: it reuses native item7’s display slot and has no direct relationship with the native self-check
So title’s position can be understood like this: strictly speaking it’s “config download + liveness probe” and doesn’t belong to native boot initialisation, but because the cabinet reaches this step every time it starts and gets checked in the same way, in a broad sense it can count as part of the boot self-check. It’s not within this article’s judgement system for amdaemon’s native 9 network self-checks; it’s the part that comes after the network self-check passes
5. Architecture class names
5.1 networktest checker layer
| item | class name | key vftable slots |
|---|---|---|
| 0 | am::networktest::IpAddressChecker | shouldRun / update |
| 1 | am::networktest::GatewayChecker | shouldRun / update |
| 2 | am::networktest::DnsLanChecker | shouldRun / update |
| 3 | am::networktest::HopsChecker | shouldRun / update |
| 4 | am::networktest::LineTypeChecker | shouldRun / update |
| 5 | am::networktest::AllnetAuthChecker | shouldRun / update |
| 6 | am::networktest::AimeChecker | shouldRun / update |
| 7 | am::networktest::EmoneyChecker | shouldRun / update |
| 8 | am::networktest::AimePayChecker | shouldRun / update |
Each item’s vftable has 6 valid slots: dtor / shouldRun / generic ×2 / helper / update. Among them shouldRun (slot1) decides whether to run, and update (slot5) drives the engine’s stepping and writes the result
5.2 am::network engine layer
| engine class | corresponding function |
|---|---|
| am::network::IpAddressChecker | IP address validity check |
| am::network::GatewayChecker(: GatewayPreChecker) | ICMP ping the gateway |
| am::network::DnsLanChecker | DNS config count validation |
| am::network::HopsCounter / NameResolver | domain resolution + route hop count |
| am::network::LocationRouterChecker | line type (LAN/WAN) determination |
| alw_auth | the whole ALL.Net auth chain |
| AimeNetworkClient | CHIME client liveness probe |
| EmoneyNetworkClient | Emoney liveness probe (dead item on the mainland server) |
| AimePayCheckInSequencer | AimePay liveness probe (dead item on the mainland server) |
5.3 Underlying library responsibilities
| library | function |
|---|---|
| alw_auth | ALL.Net auth, download instruction retrieval |
| alw_network | network stage initialisation |
| alw_dns | DNS resolution (alwDnsExecServer) |
| altr_icmp | ICMP / ping (underlying layer for the gateway and hop count) |
| alpb_stream_ssl | SSL connection |
| alib_pv2_lite(ALPB) | boot self-check state machine driver |
6. Dependency classification and troubleshooting
6.1 Dependency classification
| item | Fails if the cabinet is offline | Fails if the server doesn’t respond | Hard timeout |
|---|---|---|---|
| 0 IP address | ✅ | — | 15s |
| 1 Gateway | ✅ | — | — |
| 2 Local DNS | ✅ | — | — |
| 3 Hops | ✅ | ✅ (domain unreachable) | — |
| 4 Line type | ✅ | — | — |
| 5 Net auth | ✅ | ✅ | 120s |
| 6 CHIME | ✅ | ✅ | — |
| 7 Emoney | — | — | dead item on the mainland server, NA |
| 8 AimePay | — | — | dead item on the mainland server, NA |
6.2 What the on-screen statuses mean
- Check ( 初始化中, blinking): the engine is still waiting for a result. A long-lasting Check is mostly seen on item5 / item6; you need to confirm server connectivity
- Bad ( 坏 ): this item’s judgement failed. An item5 Bad blocks subsequent flows; a Bad on the other items doesn’t directly block the flow
- NA: this item doesn’t take part in the check. item7 / item8 are always NA on the mainland server, and item0 is also NA in
ignore_unconnectedmode
6.3 Troubleshooting directions
item 0–4 failing on the LAN: check the cabinet’s own network configuration: whether the network cable is connected, whether DHCP is delivering IP and DNS normally, whether the gateway address is configured correctly, whether the line is plugged into the LAN
item 5 (Net auth) failing or stuck in 初始化中 for a long time: a connectivity problem between the cabinet and the ALL.Net authentication server. Check whether the cabinet is connected to the internet, and whether the server is online; you can use isMaiDown to check the server’s online status
item 6 (CHIME) failing: a connectivity problem with the CHIME WeChat AIME service. Check the CHIME server’s status and the network path from the cabinet to the server; the site just mentioned is recommended
item 7 / 8: dead items on the mainland server
6.4 Master table of the 9 judgement criteria
| item | Checker | Judgement condition | Pass/Fail |
|---|---|---|---|
| 0 | IpAddressChecker | Local IP isn’t APIPA (169.254.x.x) and is in a valid subnet | OK / NG |
| 1 | GatewayChecker | ICMP ping to the gateway succeeds | OK / NG |
| 2 | DnsLanChecker | The number of discoverable DNS servers meets the configuration requirement | OK / NG |
| 3 | HopsChecker | The target domain resolves via DNS and the ICMP hop-by-hop completes | OK / NG |
| 4 | LineTypeChecker | The line type is LAN | OK / NG |
| 5 | AllnetAuthChecker | Auth passed and the download instruction is ready | OK / timeout Bad |
| 6 | AimeChecker | The CHIME liveness result is normal | OK / NG |
| 7 | EmoneyChecker | Dead item on the mainland server, shouldRun is false | NA |
| 8 | AimePayChecker | Dead item on the mainland server, shouldRun is false | NA |
Appendix
Wahlap server status monitor: Is the Wahlap server dead? - isMaiDown?
